mirror of
https://github.com/penpot/penpot.git
synced 2025-07-14 16:07:24 +02:00
🐛 Add fixes for subscription design review (#6751)
* 🐛 Fix from subscription design review * 📎 Fixes PR feedback
This commit is contained in:
parent
67ca8ccb22
commit
1f42f032fc
24 changed files with 556 additions and 511 deletions
|
@ -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
|
||||
}
|
||||
]
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
|
@ -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;
|
||||
|
|
|
@ -317,3 +317,5 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||
.click(clickOptions);
|
||||
}
|
||||
}
|
||||
|
||||
export default WorkspacePage;
|
|
@ -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();
|
||||
});
|
||||
});
|
111
frontend/playwright/ui/specs/subscriptions-profile.spec.js
Normal file
111
frontend/playwright/ui/specs/subscriptions-profile.spec.js
Normal 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();
|
||||
});
|
||||
});
|
152
frontend/playwright/ui/specs/subscriptions-workspace.spec.js
Normal file
152
frontend/playwright/ui/specs/subscriptions-workspace.spec.js
Normal 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 |
|
@ -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}]
|
||||
|
|
|
@ -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)))))
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -18,4 +18,6 @@
|
|||
|
||||
.cta-title {
|
||||
color: var(--color-foreground-primary);
|
||||
line-height: 1.2;
|
||||
margin-block-end: var(--sp-m);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)}]]])]))
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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."
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue