🐛 Add fixes for subscription design review (#6751)

* 🐛 Fix from subscription design review

* 📎 Fixes PR feedback
This commit is contained in:
Marina López 2025-06-25 13:41:45 +02:00 committed by GitHub
parent 67ca8ccb22
commit 1f42f032fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 556 additions and 511 deletions

View file

@ -2,11 +2,13 @@
{
"~:id": "~uf88e52d7-2b77-81fd-8006-23413fafe56c",
"~:name": "The Alpaca team",
"~:total-editors": 3,
"~:total-members": 3
},
{
"~:id": "~u81be1d05-a07b-81d5-8006-3e728bea76fb",
"~:name": "The Quokka team",
"~:total-editors": 1,
"~:total-members": 1
}
]

View file

@ -49,7 +49,7 @@
"~:fullname": "Chewbacca",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": false,
"~:can-edit": true,
"~:is-active": true,
"~:id": "~u4b2c3d4e-2345-6789-8002-bcdefabcdefa",
"~:profile-id": "~u4b2c3d4e-2345-6789-8002-bcdefabcdefa",
@ -77,7 +77,7 @@
"~:fullname": "R2-D2",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": false,
"~:can-edit": true,
"~:is-active": true,
"~:id": "~u6d4e5f6a-4567-8901-8004-defabcdefabc",
"~:profile-id": "~u6d4e5f6a-4567-8901-8004-defabcdefabc",
@ -91,7 +91,7 @@
"~:fullname": "C-3PO",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": false,
"~:can-edit": true,
"~:is-active": true,
"~:id": "~u7e5f6a7b-5678-9012-8005-efabcdefabcd",
"~:profile-id": "~u7e5f6a7b-5678-9012-8005-efabcdefabcd",
@ -119,7 +119,7 @@
"~:fullname": "Yoda",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": false,
"~:can-edit": true,
"~:is-active": true,
"~:id": "~u9a7b8c9d-7890-1234-8007-abcdefabcdef",
"~:profile-id": "~u9a7b8c9d-7890-1234-8007-abcdefabcdef",

View file

@ -26,5 +26,19 @@
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:profile-id": "~uf56647eb-19a7-8115-8003-b6bc939ecd1b",
"~:created-at": "~m1713533116365"
},
{
"~:is-admin": false,
"~:email": "luke@example.com",
"~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
"~:name": "Luke Skywalker",
"~:fullname": "Luke Skywalker",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": true,
"~:is-active": true,
"~:id": "~u123456789-0000-0000-0000-abcdefabcdef",
"~:profile-id": "~u123456789-0000-0000-0000-abcdefabcdef",
"~:created-at": "~m1713533116365"
}
]

View file

@ -26,5 +26,19 @@
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:profile-id": "~uf56647eb-19a7-8115-8003-b6bc939ecd1b",
"~:created-at": "~m1713533116365"
},
{
"~:is-admin": false,
"~:email": "luke@example.com",
"~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
"~:name": "Luke Skywalker",
"~:fullname": "Luke Skywalker",
"~:is-owner": false,
"~:modified-at": "~m1713533116365",
"~:can-edit": true,
"~:is-active": true,
"~:id": "~u123456789-0000-0000-0000-abcdefabcdef",
"~:profile-id": "~u123456789-0000-0000-0000-abcdefabcdef",
"~:created-at": "~m1713533116365"
}
]

View file

@ -1,52 +0,0 @@
[
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:owner",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:name": "Default",
"~:modified-at": "~m1713533116375",
"~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
"~:created-at": "~m1713533116375",
"~:is-default": true
},
{
"~:features": {
"~#set": [
"layout/grid",
"styles/v2",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:owner",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true
},
"~:subscription": {
"~:type": "unlimited",
"~:status": "paused"
},
"~:name": "Second team",
"~:modified-at": "~m1701164272671",
"~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
"~:created-at": "~m1701164272671",
"~:is-default": false
}
]

View file

@ -1,31 +1,30 @@
import { expect } from "@playwright/test";
import { BaseWebSocketPage } from "./BaseWebSocketPage";
import { DashboardPage } from "./DashboardPage";
export class SubscriptionProfilePage extends BaseWebSocketPage {
export class SubscriptionProfilePage extends DashboardPage {
static async init(page) {
await BaseWebSocketPage.initWebSockets(page);
await DashboardPage.initWebSockets(page);
await BaseWebSocketPage.mockRPC(
await DashboardPage.mockRPC(
page,
"get-owned-teams",
"subscription/get-owned-teams.json",
);
}
constructor(page) {
super(page);
this.mainHeading = page.getByRole('heading', { name: 'Subscription', level: 2 });
this.mainHeading = page.getByRole("heading", {
name: "Subscription",
level: 2,
});
}
async goToSubscriptions() {
await this.page.goto(
`#/settings/subscriptions`,
);
await this.page.goto(`#/settings/subscriptions`);
await expect(this.mainHeading).toBeVisible();
}
}
export default SubscriptionProfilePage;

View file

@ -317,3 +317,5 @@ export class WorkspacePage extends BaseWebSocketPage {
.click(clickOptions);
}
}
export default WorkspacePage;

View file

@ -1,14 +1,18 @@
import { test, expect } from "@playwright/test";
import DashboardPage from "../pages/DashboardPage";
import { WorkspacePage } from "../pages/WorkspacePage";
import { SubscriptionProfilePage } from "../pages/SubscriptionProfilePage";
test.beforeEach(async ({ page }) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, [
"enable-subscriptions",
"disable-onboarding",
]);
});
test.describe("Subscriptions: dashboard", () => {
test("Team with unlimited subscription has specific icon in menu", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -45,8 +49,6 @@ test.describe("Subscriptions: dashboard", () => {
test("The Unlimited subscription has its name in the sidebar dropdown", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -79,12 +81,10 @@ test.describe("Subscriptions: dashboard", () => {
});
});
test.describe("Subscriptions: Team members and invitations", () => {
test.describe("Subscriptions: team members and invitations", () => {
test("Team settings has susbscription name and no manage subscription link when is member", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -132,8 +132,6 @@ test.describe("Subscriptions: Team members and invitations", () => {
test("Team settings has susbscription name and manage subscription link when is owner", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -188,8 +186,6 @@ test.describe("Subscriptions: Team members and invitations", () => {
test("Members tab has warning message when team has more members than subscriptions. Subscribe link is shown for owners.", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -235,8 +231,6 @@ test.describe("Subscriptions: Team members and invitations", () => {
test("Members tab has warning message when team has more members than subscriptions. Contact to owner is shown for members.", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -282,8 +276,6 @@ test.describe("Subscriptions: Team members and invitations", () => {
test("Members tab has warning message when has professional subscription and more than 8 members.", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -330,11 +322,9 @@ test.describe("Subscriptions: Team members and invitations", () => {
).toBeVisible();
});
test("Invitations tab has warning message when subscription is expired", async ({
test("Invitations tab has warning message when team has more members than subscriptions", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -352,7 +342,7 @@ test.describe("Subscriptions: Team members and invitations", () => {
await DashboardPage.mockRPC(
page,
"get-teams",
"subscription/get-teams-unlimited-subscription-expired-owner.json",
"subscription/get-teams-unlimited-subscription-owner.json",
);
await DashboardPage.mockRPC(
@ -382,7 +372,7 @@ test.describe("Subscriptions: Team members and invitations", () => {
await expect(page.getByTestId("cta")).toBeVisible();
await expect(
page.getByText(
"Looks like your team has grown! Your plan includes seats, but you're now using more than that.",
"Looks like your team has grown! Your plan includes 2 seats, but you're now using 3",
),
).toBeVisible();
});
@ -390,8 +380,6 @@ test.describe("Subscriptions: Team members and invitations", () => {
test("Invitations tab has warning message when has professional subscription and more than 8 members.", async ({
page,
}) => {
await DashboardPage.init(page);
await DashboardPage.mockConfigFlags(page, ["enable-subscriptions"]);
await DashboardPage.mockRPC(
page,
"get-profile",
@ -409,7 +397,7 @@ test.describe("Subscriptions: Team members and invitations", () => {
await DashboardPage.mockRPC(
page,
"get-teams",
"subscription/get-teams-unlimited-subscription-expired-owner.json",
"subscription/get-teams-unlimited-subscription-owner.json",
);
await DashboardPage.mockRPC(
@ -439,274 +427,8 @@ test.describe("Subscriptions: Team members and invitations", () => {
await expect(page.getByTestId("cta")).toBeVisible();
await expect(
page.getByText(
"Looks like your team has grown! Your plan includes seats, but you're now using more than that.",
"Looks like your team has grown! Your plan includes 2 seats, but you're now using 9",
),
).toBeVisible();
});
});
test.describe("Subscriptions: workspace", () => {
test("Unlimited team should have 'Power up your plan' link in main menu", async ({
page,
}) => {
await WorkspacePage.init(page);
await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]);
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-unlimited-subscription.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await page.getByRole("button", { name: "Main menu" }).click();
await expect(page.getByText("Power up your plan")).toBeVisible();
});
test("Enterprise team should not have 'Power up your plan' link in main menu", async ({
page,
}) => {
await WorkspacePage.init(page);
await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]);
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-enterprise-subscription.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await page.getByRole("button", { name: "Main menu" }).click();
await expect(page.getByText("Power up your plan")).not.toBeVisible();
});
test("Professional team should have 7 days autosaved versions", async ({
page,
}) => {
await WorkspacePage.init(page);
await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]);
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await page.getByLabel("History").click();
await expect(
page.getByText("Autosaved versions will be kept for 7 days."),
).toBeVisible();
});
test("Unlimited team should have 30 days autosaved versions", async ({
page,
}) => {
await WorkspacePage.init(page);
await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]);
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-unlimited-subscription.json",
);
await WorkspacePage.mockRPC(
page,
"get-teams",
"subscription/get-teams-unlimited-one-team.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await page.getByLabel("History").click();
await expect(
page.getByText("Autosaved versions will be kept for 30 days."),
).toBeVisible();
});
test("Unlimited team should have 90 days autosaved versions", async ({
page,
}) => {
await WorkspacePage.init(page);
await WorkspacePage.mockConfigFlags(page, ["enable-subscriptions"]);
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-enterprise-subscription.json",
);
await WorkspacePage.mockRPC(
page,
"get-teams",
"subscription/get-teams-enterprise-one-team.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await page.getByLabel("History").click();
await expect(
page.getByText("Autosaved versions will be kept for 90 days."),
).toBeVisible();
});
});
test.describe("Subscriptions: profile", () => {
test("When subscription is professional there is no manage subscription link", async ({
page,
}) => {
await SubscriptionProfilePage.init(page);
await SubscriptionProfilePage.mockConfigFlags(page, ["enable-subscriptions"]);
await SubscriptionProfilePage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
const subscriptionProfilePage = new SubscriptionProfilePage(page);
await subscriptionProfilePage.goToSubscriptions();
await expect(
page.getByRole("button", { name: "Manage your subscription" }),
).not.toBeVisible();
await expect(
page.getByRole("heading", { name: "Other Penpot plans", level: 3 }),
).toBeVisible();
await expect(page.getByText("$7")).toBeVisible();
await expect(page.getByText("$950")).toBeVisible();
await expect(
page.getByRole("button", { name: "Try it free for 14 days" }).first(),
).toBeVisible();
});
test("When subscription is unlimited there is manage subscription link", async ({
page,
}) => {
await SubscriptionProfilePage.init(page);
await SubscriptionProfilePage.mockConfigFlags(page, ["enable-subscriptions"]);
await SubscriptionProfilePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-unlimited-subscription.json",
);
const subscriptionProfilePage = new SubscriptionProfilePage(page);
await subscriptionProfilePage.goToSubscriptions();
await expect(
page.getByRole("button", { name: "Manage your subscription" }),
).toBeVisible();
await expect(
page.getByRole("heading", { name: "Other Penpot plans", level: 3 }),
).toBeVisible();
await expect(page.getByText("$0")).toBeVisible();
await expect(page.getByText("$950")).toBeVisible();
await expect(
page.getByRole("button", { name: "Try it free for 14 days" }).first(),
).not.toBeVisible();
await expect(
page.getByRole("button", { name: "Subscribe" }).first(),
).toBeVisible();
});
test("When subscription is enteprise there is manage subscription link", async ({
page,
}) => {
await SubscriptionProfilePage.init(page);
await SubscriptionProfilePage.mockConfigFlags(page, ["enable-subscriptions"]);
await SubscriptionProfilePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-enterprise-subscription.json",
);
const subscriptionProfilePage = new SubscriptionProfilePage(page);
await subscriptionProfilePage.goToSubscriptions();
await expect(
page.getByRole("button", { name: "Manage your subscription" }),
).toBeVisible();
await expect(
page.getByRole("heading", { name: "Other Penpot plans", level: 3 }),
).toBeVisible();
await expect(page.getByText("$0")).toBeVisible();
await expect(page.getByText("$7")).toBeVisible();
await expect(
page.getByRole("button", { name: "Try it free for 14 days" }).first(),
).not.toBeVisible();
await expect(
page.getByRole("button", { name: "Subscribe" }).first(),
).toBeVisible();
});
});

View file

@ -0,0 +1,111 @@
import { test, expect } from "@playwright/test";
import SubscriptionProfilePage from "../pages/SubscriptionProfilePage";
test.beforeEach(async ({ page }) => {
await SubscriptionProfilePage.init(page);
await SubscriptionProfilePage.mockConfigFlags(page, [
"enable-subscriptions",
"disable-onboarding",
]);
});
test.describe("Subscriptions: profile", () => {
test("When subscription is professional there is no manage subscription link", async ({
page,
}) => {
await SubscriptionProfilePage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in.json",
);
const subscriptionProfilePage = new SubscriptionProfilePage(page);
await subscriptionProfilePage.goToSubscriptions();
await expect(
page.getByRole("button", { name: "Manage your subscription" }),
).not.toBeVisible();
await expect(
page.getByRole("heading", { name: "Other Penpot plans", level: 3 }),
).toBeVisible();
await expect(page.getByText("$7")).toBeVisible();
await expect(page.getByText("$950")).toBeVisible();
await expect(
page.getByRole("button", { name: "Try it free for 14 days" }).first(),
).toBeVisible();
});
test("When subscription is unlimited there is manage subscription link", async ({
page,
}) => {
await SubscriptionProfilePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-unlimited-subscription.json",
);
const subscriptionProfilePage = new SubscriptionProfilePage(page);
await subscriptionProfilePage.goToSubscriptions();
await expect(
page.getByRole("button", { name: "Manage your subscription" }),
).toBeVisible();
await expect(
page.getByRole("heading", { name: "Other Penpot plans", level: 3 }),
).toBeVisible();
await expect(page.getByText("$0")).toBeVisible();
await expect(page.getByText("$950")).toBeVisible();
await expect(
page.getByRole("button", { name: "Try it free for 14 days" }).first(),
).not.toBeVisible();
await expect(
page.getByRole("button", { name: "Subscribe" }).first(),
).toBeVisible();
});
test("When subscription is enteprise there is manage subscription link", async ({
page,
}) => {
await SubscriptionProfilePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-enterprise-subscription.json",
);
const subscriptionProfilePage = new SubscriptionProfilePage(page);
await subscriptionProfilePage.goToSubscriptions();
await expect(
page.getByRole("button", { name: "Manage your subscription" }),
).toBeVisible();
await expect(
page.getByRole("heading", { name: "Other Penpot plans", level: 3 }),
).toBeVisible();
await expect(page.getByText("$0")).toBeVisible();
await expect(page.getByText("$7")).toBeVisible();
await expect(
page.getByRole("button", { name: "Try it free for 14 days" }).first(),
).not.toBeVisible();
await expect(
page.getByRole("button", { name: "Subscribe" }).first(),
).toBeVisible();
});
});

View file

@ -0,0 +1,152 @@
import { test, expect } from "@playwright/test";
import WorkspacePage from "../pages/WorkspacePage";
test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page);
await WorkspacePage.mockConfigFlags(page, [
"enable-subscriptions",
"disable-onboarding",
]);
});
test.describe("Subscriptions: workspace", () => {
test("Unlimited team should have 'Power up your plan' link in main menu", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-unlimited-subscription.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await page.getByRole("button", { name: "Main menu" }).click();
await expect(page.getByText("Power up your plan")).toBeVisible();
});
test("Enterprise team should not have 'Power up your plan' link in main menu", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-enterprise-subscription.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await page.getByRole("button", { name: "Main menu" }).click();
await expect(page.getByText("Power up your plan")).not.toBeVisible();
});
test("Professional team should have 7 days autosaved versions", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await page.getByLabel("History").click();
await expect(
page.getByText("Autosaved versions will be kept for 7 days."),
).toBeVisible();
});
test("Unlimited team should have 30 days autosaved versions", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-unlimited-subscription.json",
);
await WorkspacePage.mockRPC(
page,
"get-teams",
"subscription/get-teams-unlimited-one-team.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await page.getByLabel("History").click();
await expect(
page.getByText("Autosaved versions will be kept for 30 days."),
).toBeVisible();
});
test("Unlimited team should have 90 days autosaved versions", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await WorkspacePage.mockRPC(
page,
"get-profile",
"subscription/get-profile-enterprise-subscription.json",
);
await WorkspacePage.mockRPC(
page,
"get-teams",
"subscription/get-teams-enterprise-one-team.json",
);
await workspacePage.mockRPC(
"push-audit-events",
"workspace/audit-event-empty.json",
);
await workspacePage.goToWorkspace();
await workspacePage.mockRPC(
"get-file-snapshots?file-id=*",
"workspace/versions-snapshot-1.json",
);
await page.getByLabel("History").click();
await expect(
page.getByText("Autosaved versions will be kept for 90 days."),
).toBeVisible();
});
});

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 63 KiB

View file

@ -132,7 +132,7 @@
[:> team-members-page* {:team team :profile profile}]
:dashboard-invitations
[:> team-invitations-page* {:team team}]
[:> team-invitations-page* {:team team :profile profile}]
:dashboard-webhooks
[:> webhooks-page* {:team team}]

View file

@ -75,10 +75,15 @@
:has-dropdown true}])
"enterprise"
(if subscription-is-trial
[:> cta-power-up*
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
:top-description (tr "subscription.dashboard.power-up.enterprise-trial.top-title")
:has-dropdown false}]
[:> cta-power-up*
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
:top-description (tr "subscription.dashboard.power-up.enterprise-plan")
:has-dropdown false}])))
:has-dropdown false}]))))
(mf/defc team*
[{:keys [is-owner team]}]
@ -137,12 +142,13 @@
[{:keys [banner-is-expanded team profile]}]
(let [subscription (:subscription team)
subscription-name (:type subscription)
subscription-is-trial (= "trialing" (:status subscription))
is-owner (:is-owner (:permissions team))
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
mail-to-owner (str "<a href=\"" "mailto:" email-owner "\">" email-owner "</a>")
go-to-subscription (dm/str (u/join cf/public-uri "#/settings/subscriptions"))
seats (-> profile :props :subscription :quantity)
editors (count (filterv :can-edit (:members team)))
link
(if is-owner
@ -151,30 +157,25 @@
cta-title
(cond
(= "professional" subscription-name)
(and (= "professional" subscription-name) (>= editors 8))
(tr "subscription.dashboard.cta.professional-plan-designed")
subscription-is-trial
(tr "subscription.dashboard.cta.trial-plan-designed")
(= "unlimited" subscription-name)
(tr "subscription.dashboard.cta.unlimited-many-editors" (:quantity (:subscription (:props profile)))))
(and (= "unlimited" subscription-name) (< seats editors))
(tr "subscription.dashboard.cta.unlimited-many-editors" seats editors))
cta-message
(cond
(and (= "professional" subscription-name) is-owner)
(and (= "professional" subscription-name) (>= editors 8) is-owner)
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner" link)
(and (= "professional" subscription-name) (not is-owner))
(and (= "professional" subscription-name) (>= editors 8) (not is-owner))
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-member" link)
(and subscription-is-trial is-owner)
(tr "subscription.dashboard.cta.upgrade-to-full-access-owner" link)
(and (= "unlimited" subscription-name) (< seats editors) is-owner)
(tr "subscription.dashboard.cta.upgrade-more-seats-owner" link)
(and subscription-is-trial (not is-owner))
(tr "subscription.dashboard.cta.upgrade-to-full-access-member" link)
(and (= "unlimited" subscription-name) (not subscription-is-trial))
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner-more-seats" link))]
(and (= "unlimited" subscription-name) (< seats editors) (not is-owner))
(tr "subscription.dashboard.cta.upgrade-more-seats-member" link))]
[:> cta* {:class (stl/css-case ::members-cta-full-width banner-is-expanded :members-cta (not banner-is-expanded)) :title cta-title}
[:> i18n/tr-html*
@ -184,18 +185,24 @@
(defn show-subscription-members-main-banner?
[team profile]
(or
(and (= (:type (:subscription team)) "professional") (>= (count (:members team)) 8))
(and
(= (:type (:subscription team)) "unlimited")
(not (= (:status (:subscription team)) "trialing"))
(>= (count (:members team)) (:quantity (:subscription (:props profile))))
(:is-owner (:permissions team)))
(= (:status (:subscription team)) "paused")))
(defn show-subscription-invitations-main-banner?
[team]
(let [seats (-> profile :props :subscription :quantity)
editors (count (filter :can-edit (:members team)))]
(or
(and (= (:type (:subscription team)) "professional")
(>= (count (:members team)) 8))
(= (:status (:subscription team)) "paused")))
(> editors 8))
(and
(= (:type (:subscription team)) "unlimited")
(>= editors 8)
(< seats editors)))))
(defn show-subscription-members-small-banner?
[team profile]
(let [seats (-> profile :props :subscription :quantity)
editors (count (filterv :can-edit (:members team)))]
(or
(and (= (:type (:subscription team)) "professional")
(= editors 8))
(and (= (:type (:subscription team)) "unlimited")
(< editors 8)
(< seats editors)))))

View file

@ -49,7 +49,7 @@
padding-block-start: var(--sp-m);
}
.cta-bottom-section .content {
@include t.use-typography("body-small");
@include t.use-typography("body-medium");
@include buttonStyle;
color: var(--color-foreground-secondary);
display: inline-block;
@ -62,12 +62,12 @@
}
.cta-title {
@include t.use-typography("title-small");
@include t.use-typography("body-small");
margin-block-end: var(--sp-xs);
}
.cta-text {
@include t.use-typography("body-small");
@include t.use-typography("title-small");
}
.cta-bottom-section .content a {
@ -96,15 +96,16 @@
}
.team-text {
@include t.use-typography("body-large");
@include t.use-typography("title-medium");
color: var(--color-foreground-primary);
}
.manage-subscription-link {
@include buttonStyle;
@include t.use-typography("body-small");
@include t.use-typography("body-medium");
color: var(--color-accent-tertiary);
display: flex;
margin-block-start: -8px;
padding: 0;
}
@ -112,8 +113,16 @@
@extend .button-icon;
background: var(--color-background-primary);
stroke: var(--color-foreground-secondary);
border-radius: var(--sp-xs);
border: $b-1 solid var(--color-foreground-secondary);
border-radius: 6px;
border: 1.75px solid var(--color-foreground-secondary);
stroke-width: 2.25px;
width: var(--sp-xl);
height: var(--sp-xl);
svg {
block-size: var(--sp-m);
inline-size: var(--sp-m);
}
}
.menu-item {
@ -136,6 +145,10 @@
margin-block-start: var(--sp-s);
margin-inline-start: $s-68;
max-width: $s-200;
.cta-title {
line-height: 1.2;
}
}
.members-cta-full-width {
@ -148,5 +161,6 @@
a {
color: var(--color-accent-primary);
overflow-wrap: break-word;
}
}

View file

@ -25,7 +25,7 @@
[app.main.ui.dashboard.subscription :refer [team*
members-cta*
show-subscription-members-main-banner?
show-subscription-invitations-main-banner?]]
show-subscription-members-small-banner?]]
[app.main.ui.dashboard.team-form]
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.main.ui.icons :as i]
@ -551,12 +551,11 @@
[:> team-members*
{:profile profile
:team team}]
(when (and
(contains? cfg/flags :subscriptions)
(or
(and (= (:type (:subscription team)) "professional") (< (count (:members team)) 8))
(= (:status (:subscription team)) "trialing")))
[:> members-cta* {:banner-is-expanded false :team team}])]])
(show-subscription-members-small-banner? team profile))
[:> members-cta* {:banner-is-expanded false :team team :profile profile}])]])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INVITATIONS SECTION
@ -804,7 +803,7 @@
(mf/defc team-invitations-page*
{::mf/props :obj}
[{:keys [team]}]
[{:keys [team profile]}]
(mf/with-effect [team]
(dom/set-html-title
@ -821,16 +820,14 @@
:team team}]
[:section {:class (stl/css-case
:dashboard-team-invitations true
:dashboard-top-cta (show-subscription-invitations-main-banner? team))}
:dashboard-top-cta (show-subscription-members-main-banner? team profile))}
(when (and (contains? cfg/flags :subscriptions)
(show-subscription-invitations-main-banner? team))
[:> members-cta* {:banner-is-expanded true :team team}])
(show-subscription-members-main-banner? team profile))
[:> members-cta* {:banner-is-expanded true :team team :profile profile}])
[:> invitation-section* {:team team}]
(when (and (contains? cfg/flags :subscriptions)
(or
(and (= (:type (:subscription team)) "professional") (< (count (:members team)) 8))
(= (:status (:subscription team)) "trialing")))
[:> members-cta* {:banner-is-expanded false :team team}])]])
(show-subscription-members-small-banner? team profile))
[:> members-cta* {:banner-is-expanded false :team team :profile profile}])]])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WEBHOOKS SECTION

View file

@ -107,6 +107,8 @@ $grayish-red: #bfbfbf;
--color-token-border: #{$aqua-400};
--color-token-accent: #{$aqua-600};
--color-token-foreground: #{$aqua-800};
--color-badge-premium: #{$orange-500};
}
:global(.default) {
@ -148,4 +150,6 @@ $grayish-red: #bfbfbf;
--color-token-border: #{$violet-700};
--color-token-accent: #{$violet-600};
--color-token-foreground: #{$violet-300};
--color-badge-premium: #{$orange-200};
}

View file

@ -20,6 +20,7 @@
(def ^:svg-id logo-error-screen "logo-error-screen")
(def ^:svg-id login-illustration "login-illustration")
(def ^:svg-id logo-subscription "logo-subscription")
(def ^:svg-id logo-subscription-light "logo-subscription-light")
(def ^:svg-id marketing-arrows "marketing-arrows")
(def ^:svg-id marketing-exchange "marketing-exchange")
(def ^:svg-id marketing-file "marketing-file")

View file

@ -18,4 +18,6 @@
.cta-title {
color: var(--color-foreground-primary);
line-height: 1.2;
margin-block-end: var(--sp-m);
}

View file

@ -19,6 +19,7 @@
(def ^:icon logo-error-screen (icon-xref :logo-error-screen))
(def ^:icon login-illustration (icon-xref :login-illustration))
(def ^:icon logo-subscription (icon-xref :logo-subscription))
(def ^:icon logo-subscription-light (icon-xref :logo-subscription-light))
(def ^:icon brand-openid (icon-xref :brand-openid))
(def ^:icon brand-github (icon-xref :brand-github))

View file

@ -2,8 +2,10 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.math :as mth]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
[app.main.data.profile :as du]
[app.main.refs :as refs]
[app.main.repo :as rp]
[app.main.router :as rt]
@ -18,12 +20,23 @@
(mf/defc plan-card*
{::mf/props :obj}
[{:keys [card-title card-title-icon price-value price-period benefits-title benefits cta-text cta-link cta-text-trial cta-link-trial cta-text-with-icon cta-link-with-icon]}]
[{:keys [card-title
card-title-icon
price-value price-period
benefits-title benefits
cta-text
cta-link
cta-text-trial
cta-link-trial
cta-text-with-icon
cta-link-with-icon
editors]}]
[:div {:class (stl/css :plan-card)}
[:div {:class (stl/css :plan-card-header)}
[:div {:class (stl/css :plan-card-title-container)}
(when card-title-icon [:span {:class (stl/css :plan-title-icon)} card-title-icon])
[:h4 {:class (stl/css :plan-card-title)} card-title]]
[:h4 {:class (stl/css :plan-card-title)} card-title]
(when editors [:span {:class (stl/css :plan-editors)} (tr "subscription.settings.editors" editors)])]
(when (and price-value price-period)
[:div {:class (stl/css :plan-price)}
[:span {:class (stl/css :plan-price-value)} price-value]
@ -45,7 +58,7 @@
::mf/register-as :management-dialog}
[{:keys [subscription-name teams subscribe-to-trial]}]
(let [min-members* (mf/use-state (or (some->> teams (map :total-members) (apply max)) 1))
(let [min-members* (mf/use-state (or (some->> teams (map :total-editors) (apply max)) 1))
min-members (deref min-members*)
formatted-subscription-name (if subscribe-to-trial
(if (= subscription-name "unlimited")
@ -75,7 +88,7 @@
returnUrl (js/encodeURIComponent current-href)
href (dm/str "payments/subscriptions/create?type=enterprise&returnUrl=" returnUrl)]
(st/emit! (rt/nav-raw :href href))))))
handle-accept-dialog (mf/use-callback
handle-accept-dialog (mf/use-fn
(fn []
(st/emit! (ptk/event ::ev/event {::ev/name "open-subscription-management"
::ev/origin "profile"
@ -85,7 +98,7 @@
href (dm/str "payments/subscriptions/show?returnUrl=" returnUrl)]
(st/emit! (rt/nav-raw :href href)))
(modal/hide!)))
handle-close-dialog (mf/use-callback
handle-close-dialog (mf/use-fn
(fn []
(st/emit! (ptk/event ::ev/event {::ev/name "close-subscription-modal"}))
(modal/hide!)))]
@ -103,7 +116,7 @@
[:ul {:class (stl/css :teams-list)}
(for [team (js->clj teams :keywordize-keys true)]
[:li {:key (dm/str (:id team)) :class (stl/css :team-name)}
(:name team) (tr "subscription.settings.management.dialog.members" (:total-members team))])]]
(:name team) (tr "subscription.settings.management.dialog.members" (:total-editors team))])]]
[:div {:class (stl/css :modal-text)}
(tr "subscription.settings.management.dialog.no-teams")])
@ -117,8 +130,14 @@
:type "number"
:value min-members
:min 1
:max 1000
:on-change #(let [new-value (js/parseInt (.. % -target -value))]
(reset! min-members* (if (or (js/isNaN new-value) (zero? new-value)) 1 (max 1 new-value))))}]]
(reset! min-members*
(let [v (cond
(or (mth/nan? new-value) (zero? new-value)) 1
(> new-value 1000) 1000
:else (max 1 new-value))]
v)))}]]
[:div {:class (stl/css :editors-cost)}
[:span {:class (stl/css :modal-text-small)}
(tr "subscription.settings.management.dialog.price-month" min-members)]
@ -150,7 +169,8 @@
::mf/register-as :subscription-success}
[{:keys [subscription-name]}]
(let [handle-close-dialog (mf/use-callback
(let [profile (mf/deref refs/profile)
handle-close-dialog (mf/use-fn
(fn []
(st/emit! (ptk/event ::ev/event {::ev/name "subscription-success"}))
(modal/hide!)))]
@ -160,7 +180,9 @@
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} i/close]
[:div {:class (stl/css :modal-success-content)}
[:div {:class (stl/css :modal-start)}
i/logo-subscription]
(if (= "light" (:theme profile))
i/logo-subscription-light
i/logo-subscription)]
[:div {:class (stl/css :modal-end)}
[:div {:class (stl/css :modal-title)} (tr "subscription.settings.sucess.dialog.title" subscription-name)]
@ -178,6 +200,7 @@
[{:keys [profile]}]
(let [route (mf/deref refs/route)
params (:params route)
params-subscription (:subscription (:query params))
show-subscription-success-modal (and (:query params)
(or (= (:subscription (:query params)) "subscribed-to-penpot-unlimited")
(= (:subscription (:query params)) "subscribed-to-penpot-enterprise")))
@ -221,11 +244,19 @@
(mf/with-effect []
(dom/set-html-title (tr "subscription.labels")))
(mf/with-effect [show-subscription-success-modal subscription]
(when show-subscription-success-modal
(st/emit! (modal/show :subscription-success
{:subscription-name (if (= (:subscription (:query params)) "subscribed-to-penpot-unlimited")
(tr "subscription.settings.unlimited-trial-modal")
(tr "subscription.settings.enterprise-trial-modal"))})))
(st/emit!
(modal/show :subscription-success
{:subscription-name (if (= params-subscription "subscribed-to-penpot-unlimited")
(tr "subscription.settings.unlimited-trial")
(tr "subscription.settings.enterprise-trial"))})
(du/update-profile-props {:subscription
(assoc subscription :type (if (= params-subscription "subscribed-to-penpot-unlimited")
"unlimited"
"enterprise"))})
(rt/nav :settings-subscription {} {::rt/replace true}))))
[:section {:class (stl/css :dashboard-section)}
[:div {:class (stl/css :dashboard-content)}
[:h2 {:class (stl/css :title-section)} (tr "subscription.labels")]
@ -251,7 +282,8 @@
:cta-text (tr "subscription.settings.manage-your-subscription")
:cta-link go-to-payments
:cta-text-trial (tr "subscription.settings.add-payment-to-continue")
:cta-link-trial go-to-payments}]
:cta-link-trial go-to-payments
:editors (-> profile :props :subscription :quantity)}]
[:> plan-card* {:card-title (tr "subscription.settings.unlimited")
:card-title-icon i/character-u
@ -260,9 +292,19 @@
(tr "subscription.settings.unlimited.bill"),
(tr "subscription.settings.unlimited.storage")]
:cta-text (tr "subscription.settings.manage-your-subscription")
:cta-link go-to-payments}])
:cta-link go-to-payments
:editors (-> profile :props :subscription :quantity)}])
"enterprise"
(if subscription-is-trial
[:> plan-card* {:card-title (tr "subscription.settings.enterprise-trial")
:card-title-icon i/character-e
:benefits-title (tr "subscription.settings.benefits.all-professional-benefits")
:benefits [(tr "subscription.settings.enterprise.support"),
(tr "subscription.settings.enterprise.security"),
(tr "subscription.settings.enterprise.logs")]
:cta-text (tr "subscription.settings.manage-your-subscription")
:cta-link go-to-payments}]
[:> plan-card* {:card-title (tr "subscription.settings.enterprise")
:card-title-icon i/character-e
:benefits-title (tr "subscription.settings.benefits.all-professional-benefits")
@ -270,7 +312,7 @@
(tr "subscription.settings.enterprise.security"),
(tr "subscription.settings.enterprise.logs")]
:cta-text (tr "subscription.settings.manage-your-subscription")
:cta-link go-to-payments}])
:cta-link go-to-payments}]))
[:div {:class (stl/css :membership-container)}
(when subscription-member [:div {:class (stl/css :membership)}

View file

@ -57,7 +57,7 @@
}
.subscription-member {
stroke: #fdcd79ff;
stroke: var(--color-badge-premium);
}
.title-section {
@ -106,6 +106,13 @@
color: var(--color-foreground-primary);
}
.plan-editors {
@include t.use-typography("body-medium");
align-self: end;
color: var(--color-foreground-primary);
margin-block-end: 2px;
}
.plan-price-period {
@include t.use-typography("body-small");
color: var(--color-foreground-primary);
@ -126,7 +133,7 @@
}
.cta-button {
@include t.use-typography("body-small");
@include t.use-typography("body-medium");
@include buttonStyle;
color: var(--color-accent-tertiary);
display: flex;

View file

@ -27,6 +27,7 @@
[app.util.keyboard :as kbd]
[app.util.time :as dt]
[cuerdas.core :as str]
[lambdaisland.uri :as u]
[okulary.core :as l]
[rumext.v2 :as mf]))
@ -41,6 +42,18 @@
(= subscription-name "enterprise") 90
:else 7)))
(defn get-versions-warning-subtext
[team]
(let [is-owner? (-> team :permissions :is-owner)
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
go-to-subscription (dm/str (u/join cfg/public-uri "#/settings/subscriptions"))]
(if (contains? cfg/flags :subscriptions)
(if is-owner?
(tr "subscription.workspace.versions.warning.subtext-owner" go-to-subscription)
(tr "subscription.workspace.versions.warning.subtext-member" email-owner email-owner))
(tr "workspace.versions.warning.subtext" "support@penpot.app"))))
(defn group-snapshots
[data]
(->> (concat
@ -369,5 +382,4 @@
[:> i18n/tr-html*
{:tag-name "div"
:class (stl/css :cta)
:content (tr "workspace.versions.warning.subtext"
"mailto:support@penpot.app")}]]])]))
:content (get-versions-warning-subtext team)}]]])]))

View file

@ -4281,27 +4281,10 @@ msgstr ""
"The Professional plan is designed for teams of up to 8 editors (owner, "
"admin, and editor)."
#: src/app/main/ui/dashboard/subscription.cljs:157
msgid "subscription.dashboard.cta.trial-plan-designed"
msgstr ""
"The Unlimited (trial) plan is designed for teams of more than 8 editors "
"(owner, admin, and editor)."
#: src/app/main/ui/dashboard/subscription.cljs:160
msgid "subscription.dashboard.cta.unlimited-many-editors"
msgstr ""
"Looks like your team has grown! Your plan includes %s seats, but you're now "
"using more than that."
#: src/app/main/ui/dashboard/subscription.cljs:174
#, markdown
msgid "subscription.dashboard.cta.upgrade-to-full-access-member"
msgstr "Unlock full access forever. Contact with the team owner: %s"
#: src/app/main/ui/dashboard/subscription.cljs:171
#, markdown
msgid "subscription.dashboard.cta.upgrade-to-full-access-owner"
msgstr "Unlock full access forever. [Subscribe now.](%s)"
"Looks like your team has grown! Your plan includes %s seats, but you're now using %s"
#: src/app/main/ui/dashboard/subscription.cljs:168
#, markdown
@ -4315,12 +4298,16 @@ msgstr ""
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner"
msgstr ""
"Get more editors, more storage, and more autosaved versions with the "
"Unlimited or Enterprise plan. [Subscribe now.](%s)"
"Unlimited or Enterprise plan. [Subscribe now.|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:176
#, markdown
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner-more-seats"
msgstr "Please upgrade to match your usage. [Subscribe now.](%s)"
msgid "subscription.dashboard.cta.upgrade-more-seats-owner"
msgstr "Please upgrade to match your usage. [Subscribe now.|target:self](%s)"
#, markdown
msgid "subscription.dashboard.cta.upgrade-more-seats-member"
msgstr "Please upgrade to match your usage. Contact with the team owner: %s"
#: src/app/main/ui/dashboard/subscription.cljs:80
msgid "subscription.dashboard.power-up.enterprise-plan"
@ -4336,7 +4323,7 @@ msgstr "Advanced security, activity logs, dedicated support and more."
msgid "subscription.dashboard.power-up.professional.bottom-description"
msgstr ""
"Get extra editors and storage, file backup and more with the Unlimited "
"plan[Power up](%s)"
"plan[Power up|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:59
msgid "subscription.dashboard.power-up.professional.top-title"
@ -4350,7 +4337,7 @@ msgstr "Subscribe"
#: src/app/main/ui/dashboard/subscription.cljs:68
#, markdown
msgid "subscription.dashboard.power-up.trial.bottom-description"
msgstr "Enjoying your trial? Unlock full access forever.[Subscribe](%s)"
msgstr "Enjoying your trial? Unlock full access forever.[Subscribe|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:62
#, unused
@ -4365,12 +4352,15 @@ msgstr "Unlimited plan (trial)"
msgid "subscription.dashboard.power-up.unlimited-plan"
msgstr "Unlimited plan"
msgid "subscription.dashboard.power-up.enterprise-trial.top-title"
msgstr "Enterprise plan (trial)"
#: src/app/main/ui/dashboard/subscription.cljs:74
#, markdown
msgid "subscription.dashboard.power-up.unlimited.bottom-description"
msgstr ""
"Get advanced security, activity logs, dedicated support and more. Take a "
"look to the[Enterprise plan.](%s)"
"look to the[Enterprise plan.|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:70
#, unused
@ -4423,10 +4413,6 @@ msgstr "Enterprise"
msgid "subscription.settings.enterprise-trial"
msgstr "Enterprise (trial)"
#: src/app/main/ui/settings/subscription.cljs:228
msgid "subscription.settings.enterprise-trial-modal"
msgstr "Enterprise trial"
#: src/app/main/ui/settings/subscription.cljs:271, src/app/main/ui/settings/subscription.cljs:320
msgid "subscription.settings.enterprise.logs"
msgstr "Activity logs"
@ -4479,7 +4465,7 @@ msgstr "Apply %s to your teams"
#: src/app/main/ui/settings/subscription.cljs:282
msgid "subscription.settings.member-since"
msgstr "Penpot member since %s"
msgstr "Penpot member since: %s"
#: src/app/main/ui/settings/subscription.cljs:295, src/app/main/ui/settings/subscription.cljs:309, src/app/main/ui/settings/subscription.cljs:323
msgid "subscription.settings.more-information"
@ -4517,6 +4503,9 @@ msgstr "Unlimited teams of up to 8 editors"
msgid "subscription.settings.section-plan"
msgstr "Your subscription"
msgid "subscription.settings.editors"
msgstr "(x %s editors)"
#: src/app/main/ui/settings/subscription.cljs:145
msgid "subscription.settings.start-trial"
msgstr "Start free trial"
@ -4542,7 +4531,7 @@ msgstr "You are %s!"
#: src/app/main/ui/settings/subscription.cljs:278
#, fuzzy
msgid "subscription.settings.support-us-since"
msgstr "You've been supporting us with this plan since %s"
msgstr "You've been supporting us with this plan since: %s"
#: src/app/main/ui/settings/subscription.cljs:307, src/app/main/ui/settings/subscription.cljs:321
msgid "subscription.settings.try-it-free"
@ -4561,10 +4550,6 @@ msgstr "Unlimited"
msgid "subscription.settings.unlimited-trial"
msgstr "Unlimited (trial)"
#: src/app/main/ui/settings/subscription.cljs:227
msgid "subscription.settings.unlimited-trial-modal"
msgstr "Unlimited trial"
#: src/app/main/ui/settings/subscription.cljs:249, src/app/main/ui/settings/subscription.cljs:260, src/app/main/ui/settings/subscription.cljs:305
msgid "subscription.settings.unlimited.bill"
msgstr "Capped monthly bill"
@ -7866,6 +7851,18 @@ msgstr ""
"If you'd like to increase this limit, write to us at "
"[support@penpot.app](%s)"
#, markdown
msgid "subscription.workspace.versions.warning.subtext-owner"
msgstr ""
"If you'd like to increase this limit, "
"[upgrade your plan|target:self](%s)"
#, markdown
msgid "subscription.workspace.versions.warning.subtext-member"
msgstr ""
"If you'd like to increase this limit, contact with the team owner: "
"[%s](mailto)"
#: src/app/main/ui/workspace/sidebar/versions.cljs:368
msgid "workspace.versions.warning.text"
msgstr "Autosaved versions will be kept for %s days."

View file

@ -4307,29 +4307,10 @@ msgstr ""
"El plan Professional está diseñado para equipos de hasta 8 editores "
"(propietario, administrador y editor)."
#: src/app/main/ui/dashboard/subscription.cljs:157
msgid "subscription.dashboard.cta.trial-plan-designed"
msgstr ""
"El plan Unlimited (de prueba) está diseñado para equipos de más de 8 "
"editores (propietario, administrador y editor)."
#: src/app/main/ui/dashboard/subscription.cljs:160
msgid "subscription.dashboard.cta.unlimited-many-editors"
msgstr ""
"¡Parece que tu equipo ha crecido! Tu plan incluye %s asientos, pero ahora "
"estás usando más que eso."
#: src/app/main/ui/dashboard/subscription.cljs:174
#, markdown
msgid "subscription.dashboard.cta.upgrade-to-full-access-member"
msgstr ""
"Desbloquea el acceso completo para siempre. Contacta con el propietario del "
"equipo: %s"
#: src/app/main/ui/dashboard/subscription.cljs:171
#, markdown
msgid "subscription.dashboard.cta.upgrade-to-full-access-owner"
msgstr "Desbloquea el acceso completo para siempre. [Suscríbete ahora.](%s)"
"¡Parece que tu equipo ha crecido! Tu plan incluye %s asientos, pero ahora estás usando %s"
#: src/app/main/ui/dashboard/subscription.cljs:168
#, markdown
@ -4344,12 +4325,17 @@ msgstr ""
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner"
msgstr ""
"Consigue más editores, más almacenamiento y más versiones guardadas "
"automáticamente con el plan Unlimited o Enterprise. [Suscríbete ahora.](%s)"
"automáticamente con el plan Unlimited o Enterprise. [Suscríbete ahora.|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:176
#, markdown
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner-more-seats"
msgstr "Por favor, mejóralo para adaptarlo a tu uso. [Suscríbete ahora.](%s)"
msgid "subscription.dashboard.cta.upgrade-more-seats-owner"
msgstr "Por favor, mejóralo para adaptarlo a tu uso. [Suscríbete ahora.|target:self](%s)"
#, markdown
msgid "subscription.dashboard.cta.upgrade-more-seats-member"
msgstr "Por favor, mejóralo para adaptarlo a tu uso. Contacta con el "
"propietario del equipo: %s"
#: src/app/main/ui/dashboard/subscription.cljs:80
msgid "subscription.dashboard.power-up.enterprise-plan"
@ -4365,7 +4351,7 @@ msgstr "Seguridad avanzada, registros de actividad, asistencia dedicada y mucho
msgid "subscription.dashboard.power-up.professional.bottom-description"
msgstr ""
"Consigue editores y almacenamiento adicionales, copias de seguridad de "
"archivos y mucho más con el Plan Unlimited[Mejóralo](%s)"
"archivos y mucho más con el Plan Unlimited[Mejóralo|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:59
msgid "subscription.dashboard.power-up.professional.top-title"
@ -4381,7 +4367,7 @@ msgstr "Suscríbete"
msgid "subscription.dashboard.power-up.trial.bottom-description"
msgstr ""
"¿Disfrutas de la prueba? Desbloquea el acceso completo para "
"siempre.[Suscríbete](%s)"
"siempre.[Suscríbete|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:62
#, unused
@ -4394,16 +4380,19 @@ msgstr ""
msgid "subscription.dashboard.power-up.trial.top-title"
msgstr "Plan Unlimited (Prueba)"
msgid "subscription.dashboard.power-up.enterprise-trial.top-title"
msgstr "Plan Enterprise (Prueba)"
#: src/app/main/ui/dashboard/subscription.cljs:73
msgid "subscription.dashboard.power-up.unlimited-plan"
msgstr "Plan ilimitado"
msgstr "Plan Unlimited"
#: src/app/main/ui/dashboard/subscription.cljs:74
#, markdown
msgid "subscription.dashboard.power-up.unlimited.bottom-description"
msgstr ""
"Obtenga seguridad avanzada, registros de actividad, asistencia dedicada y "
"mucho más. Echa un ojo al[Plan Enterprise](%s)"
"mucho más. Echa un ojo al[Plan Enterprise|target:self](%s)"
#: src/app/main/ui/dashboard/subscription.cljs:70
#, unused
@ -4453,10 +4442,6 @@ msgstr "Enterprise"
msgid "subscription.settings.enterprise-trial"
msgstr "Enterprise (prueba)"
#: src/app/main/ui/settings/subscription.cljs:228
msgid "subscription.settings.enterprise-trial-modal"
msgstr "Enterprise de prueba"
#: src/app/main/ui/settings/subscription.cljs:271, src/app/main/ui/settings/subscription.cljs:320
msgid "subscription.settings.enterprise.logs"
msgstr "Registros de actividad"
@ -4509,7 +4494,7 @@ msgstr "Aplica %s a tus equipos"
#: src/app/main/ui/settings/subscription.cljs:282
msgid "subscription.settings.member-since"
msgstr "Miembro de penpot desde %s"
msgstr "Miembro de penpot desde: %s"
#: src/app/main/ui/settings/subscription.cljs:285
msgid "subscription.settings.other-plans"
@ -4543,6 +4528,9 @@ msgstr "Equipos ilimitados de hasta 8 redactores"
msgid "subscription.settings.section-plan"
msgstr "Tu suscripción"
msgid "subscription.settings.editors"
msgstr "(x %s editores)"
#: src/app/main/ui/settings/subscription.cljs:145
msgid "subscription.settings.start-trial"
msgstr "Comenzar prueba gratuita"
@ -4567,7 +4555,7 @@ msgstr "Eres %s!"
#: src/app/main/ui/settings/subscription.cljs:278
msgid "subscription.settings.support-us-since"
msgstr "Nos has estado apoyando con este plan desde %s"
msgstr "Nos has estado apoyando con este plan desde: %s"
#: src/app/main/ui/settings/subscription.cljs:307, src/app/main/ui/settings/subscription.cljs:321
msgid "subscription.settings.try-it-free"
@ -4586,10 +4574,6 @@ msgstr "Unlimited"
msgid "subscription.settings.unlimited-trial"
msgstr "Unlimited (prueba)"
#: src/app/main/ui/settings/subscription.cljs:227
msgid "subscription.settings.unlimited-trial-modal"
msgstr "Unlimited de prueba"
#: src/app/main/ui/settings/subscription.cljs:249, src/app/main/ui/settings/subscription.cljs:260, src/app/main/ui/settings/subscription.cljs:305
msgid "subscription.settings.unlimited.bill"
msgstr "Factura mensual limitada"
@ -7829,6 +7813,18 @@ msgstr "Abrir menu de versiones"
msgid "workspace.versions.warning.subtext"
msgstr "Si quieres aumentar este límite, contáctanos en [support@penpot.app](%s)"
#, markdown
msgid "subscription.workspace.versions.warning.subtext-owner"
msgstr ""
"Si quieres aumentar este límite, "
"[mejora tu plan|target:self](%s)"
#, markdown
msgid "subscription.workspace.versions.warning.subtext-member"
msgstr ""
"Si quieres aumentar este límite, contacta con el propietario del equipo: "
"[%s](mailto)"
#: src/app/main/ui/workspace/sidebar/versions.cljs:368
msgid "workspace.versions.warning.text"
msgstr "Los autoguardados duran %s días."