Skip to content

Rework organization page #8679

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4cd2ecd
refactor orga settings in new style
hotzenklotz Jun 6, 2025
d022413
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 16, 2025
82fd616
Refactor account settings components and update organization navigation
hotzenklotz Jun 16, 2025
aa95b90
Enhance organization management UI and functionality
hotzenklotz Jun 16, 2025
a2002cb
formatting
hotzenklotz Jun 16, 2025
b7543ea
Enhance organization-related components and UI
hotzenklotz Jun 16, 2025
749861a
fix setting cards buttons
hotzenklotz Jun 16, 2025
44e0966
changelog
hotzenklotz Jun 16, 2025
8716091
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 17, 2025
55bf833
Update frontend/javascripts/admin/organization/pricing_plan_utils.ts
hotzenklotz Jun 18, 2025
513011b
applied PR feedback
hotzenklotz Jun 18, 2025
01ae877
Merge branch 'orga-settings-page' of github.com:scalableminds/webknos…
hotzenklotz Jun 18, 2025
67e8819
Show owner email in notifications
hotzenklotz Jun 18, 2025
121c91a
fix pricing plan label
hotzenklotz Jun 18, 2025
203ae75
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 23, 2025
df8e81d
do not show upgrade options for custom plans yet
hotzenklotz Jun 23, 2025
66086aa
fix null / infinity on update
hotzenklotz Jun 24, 2025
1903218
appled PR feedback
hotzenklotz Jun 24, 2025
d61a522
apply PR feedback
hotzenklotz Jun 24, 2025
f2ee79d
fix linitng
hotzenklotz Jun 24, 2025
2ed707a
Merge branch 'account-settings-page' into orga-settings-page
hotzenklotz Jun 24, 2025
9599a88
Update frontend/javascripts/admin/organization/upgrade_plan_modal.tsx
hotzenklotz Jun 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 1 addition & 30 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
@@ -1,30 +1 @@
# Changelog (Unreleased)

All notable (yet unreleased) user-facing changes to WEBKNOSSOS are documented in this file.
See `CHANGELOG.released.md` for the changes which are part of official releases.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`.
For upgrade instructions, please check the [migration guide](MIGRATIONS.released.md).

## Unreleased
[Commits](https://github.com/scalableminds/webknossos/compare/25.06.1...HEAD)

### Added
- In shared annotations with multiple authors, some changes are now stored per user. This means that other users won’t see all those changes if their own diverge. This includes the current position and zoom, visibilities of trees, bounding boxes, and segments (as specified with the checkboxes in the lists), as well as which groups are expanded in the lists. The annotation owner’s user state is used as a fallback for users who haven’t explicitly changed these values themselves. [#8542](https://github.com/scalableminds/webknossos/pull/8542)
- Added the ability to duplicate trees in skeleton annotations. Users can create a copy of any tree (including all nodes, edges, and properties) via the context menu in the skeleton tab. [#8662](https://github.com/scalableminds/webknossos/pull/8662)
- Meshes are now reloaded using their previous opacity value. [#8622](https://github.com/scalableminds/webknossos/pull/8622)

### Changed
- Updated the UI for the organizations settings page. [#8679](https://github.com/scalableminds/webknossos/pull/8679)

### Fixed
- Improved efficiency of saving bounding box related changes. [#8492](https://github.com/scalableminds/webknossos/pull/8492)
- When deleting a dataset, its caches are cleared, so that if a new dataset by the same name is uploaded afterwards, only new data is loaded. [#8638](https://github.com/scalableminds/webknossos/pull/8638)
- Fixed the contrast of the WelcomeToast buttons. Updated `antd` to version `5.22`.[#8688](https://github.com/scalableminds/webknossos/pull/8688)
- Fixed a race condition when starting proofreading with a split action. [#8676](https://github.com/scalableminds/webknossos/pull/8676)
- Fixed that activating a mapping got stuck when a dataset was opened in "view" mode. [#8687](https://github.com/scalableminds/webknossos/pull/8687)

### Removed

### Breaking Changes
WARNING: This file will be deleted in the next release. If you have a merge conflict in this file, please ensure that the file only contains this warning text after the merge. Please execute `./tools/create-changelog-entry.py` and move the changelog for your PR into the file that was created by the script. From now on, this will be the new way of adding changelog and migration guide entries for a PR. The created files will be aggregated when a release is created.
4 changes: 2 additions & 2 deletions frontend/javascripts/admin/account/helpers/settings_card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { Card, Flex, Popover, Typography } from "antd";
interface SettingsCardProps {
title: string;
description: React.ReactNode;
explanation?: string;
explanation?: React.ReactNode;
action?: React.ReactNode;
}

export function SettingsCard({ title, description, explanation, action }: SettingsCardProps) {
return (
<Card>
<Card style={{ minHeight: 105 }}>
<Typography.Text type="secondary" style={{ fontSize: 14 }}>
<Flex justify="space-between">
<div>
Expand Down
41 changes: 1 addition & 40 deletions frontend/javascripts/admin/organization/organization_cards.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FieldTimeOutlined, MailOutlined, PlusCircleOutlined } from "@ant-design/icons";
import { FieldTimeOutlined, PlusCircleOutlined } from "@ant-design/icons";
import { Alert, Button, Card, Col, Row } from "antd";
import { formatDateInLocalTimeZone } from "components/formatted_date";
import dayjs from "dayjs";
Expand All @@ -7,39 +7,13 @@ import type { APIOrganization } from "types/api_types";
import Constants from "viewer/constants";
import {
PricingPlanEnum,
customPlanFeatures,
hasPricingPlanExpired,
isUserAllowedToRequestUpgrades,
powerPlanFeatures,
teamPlanFeatures,
} from "./pricing_plan_utils";
import UpgradePricingPlanModal from "./upgrade_plan_modal";

function CustomPlanUpgradeCard() {
return (
<Card styles={{ body: { minHeight: 220 } }}>
<Row>
<Col span={18}>
<p>
Contact our support team to upgrade Webknossos to match your organization and customized
your experience.
</p>
<ul>
{customPlanFeatures.map((feature) => (
<li key={feature.slice(0, 10)}>{feature}</li>
))}
</ul>
</Col>
<Col span={6}>
<Button type="primary" href="mailto:sales@webknossos.org" icon={<MailOutlined />}>
Contact Support
</Button>
</Col>
</Row>
</Card>
);
}

export function TeamPlanUpgradeCard({ teamUpgradeCallback }: { teamUpgradeCallback: () => void }) {
return (
<Card
Expand Down Expand Up @@ -88,19 +62,6 @@ export function PowerPlanUpgradeCard({
}

export function PlanUpgradeCard({ organization }: { organization: APIOrganization }) {
if (
organization.pricingPlan === PricingPlanEnum.Power ||
organization.pricingPlan === PricingPlanEnum.PowerTrial ||
organization.pricingPlan === PricingPlanEnum.Custom
)
return (
<Row gutter={24}>
<Col span={24}>
<CustomPlanUpgradeCard />
</Col>
</Row>
);

if (
organization.pricingPlan === PricingPlanEnum.Team ||
organization.pricingPlan === PricingPlanEnum.TeamTrial
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { MailOutlined, SaveOutlined } from "@ant-design/icons";
import { SettingsCard } from "admin/account/helpers/settings_card";
import { SettingsTitle } from "admin/account/helpers/settings_title";
import { updateOrganization } from "admin/rest_api";
import { getUsers } from "admin/rest_api";
import { Button, Col, Form, Input, Row } from "antd";
import Toast from "libs/toast";
import { useEffect, useState } from "react";
import type { APIOrganization } from "types/api_types";
import { setActiveOrganizationAction } from "viewer/model/actions/organization_actions";
import { Store } from "viewer/singletons";
Expand All @@ -17,6 +19,20 @@ type FormValues = {

export function OrganizationNotificationsView({ organization }: { organization: APIOrganization }) {
const [form] = Form.useForm<FormValues>();
const [ownerEmail, setOwnerEmail] = useState<string>("");

useEffect(() => {
async function fetchOwnerEmail() {
const users = await getUsers();
const owner = users.find(
(user) => user.isOrganizationOwner && user.organization === organization.id,
);
if (owner) {
setOwnerEmail(owner.email);
}
}
fetchOwnerEmail();
}, [organization.id]);

async function onFinish(formValues: FormValues) {
const updatedOrganization = await updateOrganization(
Expand Down Expand Up @@ -79,7 +95,7 @@ export function OrganizationNotificationsView({ organization }: { organization:
<SettingsCard
title="WEBKNOSSOS Plan & Subscription"
explanation="Get notified when your WK subscription is about to expire or reach user and storage limits."
description={organization.ownerName}
description={ownerEmail}
/>
</Col>
<Col span={12}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import {
import { PricingPlanEnum, getActiveUserCount } from "./pricing_plan_utils";
import UpgradePricingPlanModal from "./upgrade_plan_modal";

const ORGA_NAME_REGEX_PATTERN = /^[A-Za-z0-9\\-_\\. ß]+$/;

export function OrganizationOverviewView({ organization }: { organization: APIOrganization }) {
const [isFetchingData, setIsFetchingData] = useState(false);
const [isFetchingData, setIsFetchingData] = useState(true);
const [activeUsersCount, setActiveUsersCount] = useState(1);
const [pricingPlanStatus, setPricingPlanStatus] = useState<APIPricingPlanStatus | null>(null);

Expand All @@ -37,9 +39,7 @@ export function OrganizationOverviewView({ organization }: { organization: APIOr
}

async function setOrganizationName(newOrgaName: string) {
const OrgaNameRegexPattern = /^[A-Za-z0-9\\-_\\. ß]+$/;

if (!OrgaNameRegexPattern.test(newOrgaName)) {
if (!ORGA_NAME_REGEX_PATTERN.test(newOrgaName)) {
Toast.error(
"Organization name can only contain letters, numbers, spaces, and the following special characters: - _ . ß",
);
Expand All @@ -66,7 +66,6 @@ export function OrganizationOverviewView({ organization }: { organization: APIOr

let upgradeUsersAction: React.ReactNode = null;
let upgradeStorageAction: React.ReactNode = null;
let upgradePlanAction: React.ReactNode = null;

if (
organization.pricingPlan === PricingPlanEnum.Basic ||
Expand Down Expand Up @@ -100,18 +99,6 @@ export function OrganizationOverviewView({ organization }: { organization: APIOr
}
/>
);

upgradePlanAction = (
<Button
shape="circle"
size="small"
key="comparePlanAction"
icon={<PlusOutlined />}
href="https://webknossos.org/pricing"
target="_blank"
rel="noopener noreferrer"
/>
);
}
const buyMoreCreditsAction = (
<Tooltip title="Disabled during testing phase" key="buyMoreCreditsAction">
Expand Down Expand Up @@ -144,18 +131,22 @@ export function OrganizationOverviewView({ organization }: { organization: APIOr
{
key: "owner",
title: "Owner",
value: "John Doe",
value: organization.ownerName,
},
{
key: "plan",
title: "Current Plan",
value: "Basic",
action: upgradePlanAction,
value: organization.pricingPlan,
explanation: (
<a href="https://webknossos.org/pricing" target="_blank" rel="noopener noreferrer">
Compare all plans
</a>
),
},
{
key: "users",
title: "Users",
value: `${activeUsersCount}/${maxUsersCountLabel}`,
value: `${activeUsersCount} / ${maxUsersCountLabel}`,
action: upgradeUsersAction,
},
{
Expand Down Expand Up @@ -184,17 +175,28 @@ export function OrganizationOverviewView({ organization }: { organization: APIOr
<Row gutter={[24, 24]} style={{ marginBottom: 24 }}>
{orgaStats.map((stat) => (
<Col span={8} key={stat.key}>
<SettingsCard title={stat.title} description={stat.value} action={stat.action} />
<SettingsCard
title={stat.title}
description={stat.value}
action={stat.action}
explanation={stat.explanation}
/>
</Col>
))}
</Row>
</Spin>
<PlanExpirationCard organization={organization} />
<SettingsTitle
title="Unlock more features"
description="Upgrade your organization to unlock more collaboration and proofreading features for your team."
/>
<PlanUpgradeCard organization={organization} />
{organization.pricingPlan === PricingPlanEnum.Basic ||
organization.pricingPlan === PricingPlanEnum.Team ||
organization.pricingPlan === PricingPlanEnum.TeamTrial ? (
<>
<SettingsTitle
title="Unlock more features"
description="Upgrade your organization to unlock more collaboration and proofreading features for your team."
/>
<PlanUpgradeCard organization={organization} />
</>
) : null}
</>
);
}
Loading