diff --git a/frontend/playwright/data/subscription/get-owned-teams.json b/frontend/playwright/data/subscription/get-owned-teams.json
index e714a37a7c..7d925f91fc 100644
--- a/frontend/playwright/data/subscription/get-owned-teams.json
+++ b/frontend/playwright/data/subscription/get-owned-teams.json
@@ -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
}
]
\ No newline at end of file
diff --git a/frontend/playwright/data/subscription/get-team-members-more-than-8.json b/frontend/playwright/data/subscription/get-team-members-more-than-8.json
index 55210eeff2..5679420fb1 100644
--- a/frontend/playwright/data/subscription/get-team-members-more-than-8.json
+++ b/frontend/playwright/data/subscription/get-team-members-more-than-8.json
@@ -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",
diff --git a/frontend/playwright/data/subscription/get-team-members-subscription-member.json b/frontend/playwright/data/subscription/get-team-members-subscription-member.json
index 04237e11f8..6296730aa5 100644
--- a/frontend/playwright/data/subscription/get-team-members-subscription-member.json
+++ b/frontend/playwright/data/subscription/get-team-members-subscription-member.json
@@ -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"
}
]
diff --git a/frontend/playwright/data/subscription/get-team-members-subscription-owner.json b/frontend/playwright/data/subscription/get-team-members-subscription-owner.json
index 16bbf41d8d..7e47c4f6aa 100644
--- a/frontend/playwright/data/subscription/get-team-members-subscription-owner.json
+++ b/frontend/playwright/data/subscription/get-team-members-subscription-owner.json
@@ -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"
}
]
diff --git a/frontend/playwright/data/subscription/get-teams-unlimited-subscription-expired-owner.json b/frontend/playwright/data/subscription/get-teams-unlimited-subscription-expired-owner.json
deleted file mode 100644
index a74b844fb1..0000000000
--- a/frontend/playwright/data/subscription/get-teams-unlimited-subscription-expired-owner.json
+++ /dev/null
@@ -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
- }
-]
diff --git a/frontend/playwright/ui/pages/SubscriptionProfilePage.js b/frontend/playwright/ui/pages/SubscriptionProfilePage.js
index d37e625045..304856fdb4 100644
--- a/frontend/playwright/ui/pages/SubscriptionProfilePage.js
+++ b/frontend/playwright/ui/pages/SubscriptionProfilePage.js
@@ -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;
diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js
index a57fe35b6d..c85010edc1 100644
--- a/frontend/playwright/ui/pages/WorkspacePage.js
+++ b/frontend/playwright/ui/pages/WorkspacePage.js
@@ -317,3 +317,5 @@ export class WorkspacePage extends BaseWebSocketPage {
.click(clickOptions);
}
}
+
+export default WorkspacePage;
\ No newline at end of file
diff --git a/frontend/playwright/ui/specs/subscriptions.spec.js b/frontend/playwright/ui/specs/subscriptions-dashboard.spec.js
similarity index 54%
rename from frontend/playwright/ui/specs/subscriptions.spec.js
rename to frontend/playwright/ui/specs/subscriptions-dashboard.spec.js
index 58742bb1d5..b14abffd48 100644
--- a/frontend/playwright/ui/specs/subscriptions.spec.js
+++ b/frontend/playwright/ui/specs/subscriptions-dashboard.spec.js
@@ -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();
- });
-});
diff --git a/frontend/playwright/ui/specs/subscriptions-profile.spec.js b/frontend/playwright/ui/specs/subscriptions-profile.spec.js
new file mode 100644
index 0000000000..4f93070fcb
--- /dev/null
+++ b/frontend/playwright/ui/specs/subscriptions-profile.spec.js
@@ -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();
+ });
+});
diff --git a/frontend/playwright/ui/specs/subscriptions-workspace.spec.js b/frontend/playwright/ui/specs/subscriptions-workspace.spec.js
new file mode 100644
index 0000000000..237f4354b3
--- /dev/null
+++ b/frontend/playwright/ui/specs/subscriptions-workspace.spec.js
@@ -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();
+ });
+});
diff --git a/frontend/resources/images/icons/logo-subscription-light.svg b/frontend/resources/images/icons/logo-subscription-light.svg
new file mode 100644
index 0000000000..838ca4b8cd
--- /dev/null
+++ b/frontend/resources/images/icons/logo-subscription-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs
index 05c523fa29..7ab47029fb 100644
--- a/frontend/src/app/main/ui/dashboard.cljs
+++ b/frontend/src/app/main/ui/dashboard.cljs
@@ -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}]
diff --git a/frontend/src/app/main/ui/dashboard/subscription.cljs b/frontend/src/app/main/ui/dashboard/subscription.cljs
index 62ea6f6f2e..69f8402b04 100644
--- a/frontend/src/app/main/ui/dashboard/subscription.cljs
+++ b/frontend/src/app/main/ui/dashboard/subscription.cljs
@@ -75,10 +75,15 @@
:has-dropdown true}])
"enterprise"
- [:> cta-power-up*
- {:top-title (tr "subscription.dashboard.power-up.your-subscription")
- :top-description (tr "subscription.dashboard.power-up.enterprise-plan")
- :has-dropdown false}])))
+ (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}]))))
(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 "" email-owner "")
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")))
+ (let [seats (-> profile :props :subscription :quantity)
+ editors (count (filter :can-edit (:members team)))]
+ (or
+ (and (= (:type (:subscription team)) "professional")
+ (> 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)))))
-(defn show-subscription-invitations-main-banner?
- [team]
- (or
- (and (= (:type (:subscription team)) "professional")
- (>= (count (:members team)) 8))
- (= (:status (:subscription team)) "paused")))
diff --git a/frontend/src/app/main/ui/dashboard/subscription.scss b/frontend/src/app/main/ui/dashboard/subscription.scss
index 3f203b049d..f864bbf419 100644
--- a/frontend/src/app/main/ui/dashboard/subscription.scss
+++ b/frontend/src/app/main/ui/dashboard/subscription.scss
@@ -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;
}
}
diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs
index 76ab3cd6f5..b6f0b50ae4 100644
--- a/frontend/src/app/main/ui/dashboard/team.cljs
+++ b/frontend/src/app/main/ui/dashboard/team.cljs
@@ -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
diff --git a/frontend/src/app/main/ui/ds/colors.scss b/frontend/src/app/main/ui/ds/colors.scss
index ce535caa48..d3f01a8a16 100644
--- a/frontend/src/app/main/ui/ds/colors.scss
+++ b/frontend/src/app/main/ui/ds/colors.scss
@@ -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};
}
diff --git a/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs b/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs
index f9e4f37298..a1b5199aa1 100644
--- a/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs
+++ b/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs
@@ -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")
diff --git a/frontend/src/app/main/ui/ds/product/cta.scss b/frontend/src/app/main/ui/ds/product/cta.scss
index f35baf278b..630b8875e4 100644
--- a/frontend/src/app/main/ui/ds/product/cta.scss
+++ b/frontend/src/app/main/ui/ds/product/cta.scss
@@ -18,4 +18,6 @@
.cta-title {
color: var(--color-foreground-primary);
+ line-height: 1.2;
+ margin-block-end: var(--sp-m);
}
diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs
index 37bf248a40..b344a4aebe 100644
--- a/frontend/src/app/main/ui/icons.cljs
+++ b/frontend/src/app/main/ui/icons.cljs
@@ -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))
diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs
index 444da8dc1c..f03477e374 100644
--- a/frontend/src/app/main/ui/settings/subscription.cljs
+++ b/frontend/src/app/main/ui/settings/subscription.cljs
@@ -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")))
- (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"))})))
+ (mf/with-effect [show-subscription-success-modal subscription]
+ (when show-subscription-success-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,17 +292,27 @@
(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"
- [:> plan-card* {:card-title (tr "subscription.settings.enterprise")
- :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}])
+ (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")
+ :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}]))
[:div {:class (stl/css :membership-container)}
(when subscription-member [:div {:class (stl/css :membership)}
diff --git a/frontend/src/app/main/ui/settings/subscription.scss b/frontend/src/app/main/ui/settings/subscription.scss
index d1903f1b88..b077874fd5 100644
--- a/frontend/src/app/main/ui/settings/subscription.scss
+++ b/frontend/src/app/main/ui/settings/subscription.scss
@@ -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;
diff --git a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs
index 7c0f971890..65b0f13eae 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs
@@ -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)}]]])]))
diff --git a/frontend/translations/en.po b/frontend/translations/en.po
index 374ca80637..716d147413 100644
--- a/frontend/translations/en.po
+++ b/frontend/translations/en.po
@@ -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."
diff --git a/frontend/translations/es.po b/frontend/translations/es.po
index 4c1f4486ee..a750e23e5c 100644
--- a/frontend/translations/es.po
+++ b/frontend/translations/es.po
@@ -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."