mirror of
https://github.com/penpot/penpot.git
synced 2025-07-18 13:07:14 +02:00
🐛 Add fixes from subscription design review (#6870)
* 🐛 Fixes from subscription design review * 🐛 Fix to consider professional plan the unpaid and canceled status * 📎 Fixes PR feedback
This commit is contained in:
parent
66c5841d48
commit
ba6a02d1d9
20 changed files with 312 additions and 154 deletions
|
@ -140,7 +140,7 @@
|
||||||
WHEN 'professional' THEN 'active'
|
WHEN 'professional' THEN 'active'
|
||||||
ELSE COALESCE(p.props->'~:subscription'->>'~:status', 'incomplete')
|
ELSE COALESCE(p.props->'~:subscription'->>'~:status', 'incomplete')
|
||||||
END,
|
END,
|
||||||
'~:seats', p.props->'~:quantity'
|
'~:seats', p.props->'~:subscription'->'~:quantity'
|
||||||
) AS subscription
|
) AS subscription
|
||||||
FROM team_profile_rel AS tp
|
FROM team_profile_rel AS tp
|
||||||
JOIN team AS t ON (t.id = tp.team_id)
|
JOIN team AS t ON (t.id = tp.team_id)
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"~:email": "foo@example.com",
|
||||||
|
"~:is-demo": false,
|
||||||
|
"~:auth-backend": "penpot",
|
||||||
|
"~:fullname": "Princesa Leia",
|
||||||
|
"~:modified-at": "~m1713533116365",
|
||||||
|
"~:is-active": true,
|
||||||
|
"~:default-project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
|
||||||
|
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
|
||||||
|
"~:is-muted": false,
|
||||||
|
"~:default-team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||||
|
"~:created-at": "~m1713533116365",
|
||||||
|
"~:is-blocked": false,
|
||||||
|
"~:props": {
|
||||||
|
"~:subscription": {
|
||||||
|
"~:quantity": 2,
|
||||||
|
"~:status": "canceled",
|
||||||
|
"~:type": "enterprise",
|
||||||
|
"~start-date": "~m1746444667"
|
||||||
|
},
|
||||||
|
"~:v2-info-shown": true,
|
||||||
|
"~:viewed-tutorial?": false,
|
||||||
|
"~:viewed-walkthrough?": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"~:email": "foo@example.com",
|
||||||
|
"~:is-demo": false,
|
||||||
|
"~:auth-backend": "penpot",
|
||||||
|
"~:fullname": "Princesa Leia",
|
||||||
|
"~:modified-at": "~m1713533116365",
|
||||||
|
"~:is-active": true,
|
||||||
|
"~:default-project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
|
||||||
|
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
|
||||||
|
"~:is-muted": false,
|
||||||
|
"~:default-team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||||
|
"~:created-at": "~m1713533116365",
|
||||||
|
"~:is-blocked": false,
|
||||||
|
"~:props": {
|
||||||
|
"~:subscription": {
|
||||||
|
"~:quantity": 2,
|
||||||
|
"~:status": "unpaid",
|
||||||
|
"~:type": "unlimited",
|
||||||
|
"~start-date": "~m1746444667"
|
||||||
|
},
|
||||||
|
"~:v2-info-shown": true,
|
||||||
|
"~:viewed-tutorial?": false,
|
||||||
|
"~:viewed-walkthrough?": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,8 @@
|
||||||
},
|
},
|
||||||
"~:subscription": {
|
"~:subscription": {
|
||||||
"~:type": "enterprise",
|
"~:type": "enterprise",
|
||||||
"~:status": "trialing"
|
"~:status": "trialing",
|
||||||
|
"~:seats": null
|
||||||
},
|
},
|
||||||
"~:name": "Default",
|
"~:name": "Default",
|
||||||
"~:modified-at": "~m1713533116375",
|
"~:modified-at": "~m1713533116375",
|
||||||
|
|
|
@ -41,7 +41,8 @@
|
||||||
},
|
},
|
||||||
"~:subscription": {
|
"~:subscription": {
|
||||||
"~:type": "professional",
|
"~:type": "professional",
|
||||||
"~:status": "active"
|
"~:status": "active",
|
||||||
|
"~:seats": null
|
||||||
},
|
},
|
||||||
"~:name": "Second team",
|
"~:name": "Second team",
|
||||||
"~:modified-at": "~m1701164272671",
|
"~:modified-at": "~m1701164272671",
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
},
|
},
|
||||||
"~:subscription": {
|
"~:subscription": {
|
||||||
"~:type": "unlimited",
|
"~:type": "unlimited",
|
||||||
"~:status": "trialing"
|
"~:status": "trialing",
|
||||||
|
"~:seats": 2
|
||||||
},
|
},
|
||||||
"~:name": "Default",
|
"~:name": "Default",
|
||||||
"~:modified-at": "~m1713533116375",
|
"~:modified-at": "~m1713533116375",
|
||||||
|
|
|
@ -41,7 +41,8 @@
|
||||||
},
|
},
|
||||||
"~:subscription": {
|
"~:subscription": {
|
||||||
"~:type": "unlimited",
|
"~:type": "unlimited",
|
||||||
"~:status": "trialing"
|
"~:status": "trialing",
|
||||||
|
"~:seats": 2
|
||||||
},
|
},
|
||||||
"~:name": "Second team",
|
"~:name": "Second team",
|
||||||
"~:modified-at": "~m1701164272671",
|
"~:modified-at": "~m1701164272671",
|
||||||
|
|
|
@ -41,7 +41,8 @@
|
||||||
},
|
},
|
||||||
"~:subscription": {
|
"~:subscription": {
|
||||||
"~:type": "unlimited",
|
"~:type": "unlimited",
|
||||||
"~:status": "trialing"
|
"~:status": "trialing",
|
||||||
|
"~:seats": 2
|
||||||
},
|
},
|
||||||
"~:name": "Second team",
|
"~:name": "Second team",
|
||||||
"~:modified-at": "~m1701164272671",
|
"~:modified-at": "~m1701164272671",
|
||||||
|
|
|
@ -79,6 +79,74 @@ test.describe("Subscriptions: dashboard", () => {
|
||||||
"Unlimited plan (trial)",
|
"Unlimited plan (trial)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("When the subscription status is unpaid, the sidebar dropdown displays the name Professional for the Unlimited subscription", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await DashboardPage.mockRPC(
|
||||||
|
page,
|
||||||
|
"get-profile",
|
||||||
|
"subscription/get-profile-unlimited-unpaid-subscription.json",
|
||||||
|
);
|
||||||
|
|
||||||
|
await DashboardPage.mockRPC(
|
||||||
|
page,
|
||||||
|
"get-team-info",
|
||||||
|
"subscription/get-team-info-subscriptions.json",
|
||||||
|
);
|
||||||
|
|
||||||
|
const dashboardPage = new DashboardPage(page);
|
||||||
|
await dashboardPage.setupDashboardFull();
|
||||||
|
await DashboardPage.mockRPC(
|
||||||
|
page,
|
||||||
|
"get-teams",
|
||||||
|
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||||
|
);
|
||||||
|
|
||||||
|
await dashboardPage.mockRPC(
|
||||||
|
"push-audit-events",
|
||||||
|
"workspace/audit-event-empty.json",
|
||||||
|
);
|
||||||
|
await dashboardPage.goToDashboard();
|
||||||
|
|
||||||
|
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||||
|
"Professional plan",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("When the subscription status is canceled, the sidebar dropdown displays the name Professional for the Enterprise subscription", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await DashboardPage.mockRPC(
|
||||||
|
page,
|
||||||
|
"get-profile",
|
||||||
|
"subscription/get-profile-enterprise-canceled-subscription.json",
|
||||||
|
);
|
||||||
|
|
||||||
|
await DashboardPage.mockRPC(
|
||||||
|
page,
|
||||||
|
"get-team-info",
|
||||||
|
"subscription/get-team-info-subscriptions.json",
|
||||||
|
);
|
||||||
|
|
||||||
|
const dashboardPage = new DashboardPage(page);
|
||||||
|
await dashboardPage.setupDashboardFull();
|
||||||
|
await DashboardPage.mockRPC(
|
||||||
|
page,
|
||||||
|
"get-teams",
|
||||||
|
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||||
|
);
|
||||||
|
|
||||||
|
await dashboardPage.mockRPC(
|
||||||
|
"push-audit-events",
|
||||||
|
"workspace/audit-event-empty.json",
|
||||||
|
);
|
||||||
|
await dashboardPage.goToDashboard();
|
||||||
|
|
||||||
|
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||||
|
"Professional plan",
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Subscriptions: team members and invitations", () => {
|
test.describe("Subscriptions: team members and invitations", () => {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// INPUT
|
// INPUT
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
|
--input-icon-padding: var(--sp-l);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -108,7 +109,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: $s-16;
|
right: var(--input-icon-padding);
|
||||||
top: calc(50% - $s-8);
|
top: calc(50% - $s-8);
|
||||||
svg {
|
svg {
|
||||||
width: $s-12;
|
width: $s-12;
|
||||||
|
|
|
@ -132,7 +132,7 @@
|
||||||
[:> team-members-page* {:team team :profile profile}]
|
[:> team-members-page* {:team team :profile profile}]
|
||||||
|
|
||||||
:dashboard-invitations
|
:dashboard-invitations
|
||||||
[:> team-invitations-page* {:team team :profile profile}]
|
[:> team-invitations-page* {:team team}]
|
||||||
|
|
||||||
:dashboard-webhooks
|
:dashboard-webhooks
|
||||||
[:> webhooks-page* {:team team}]
|
[:> webhooks-page* {:team team}]
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
|
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
|
||||||
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
|
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
|
||||||
[app.main.ui.dashboard.project-menu :refer [project-menu*]]
|
[app.main.ui.dashboard.project-menu :refer [project-menu*]]
|
||||||
[app.main.ui.dashboard.subscription :refer [subscription-sidebar* menu-team-icon*]]
|
[app.main.ui.dashboard.subscription :refer [subscription-sidebar* menu-team-icon* get-subscription-type]]
|
||||||
[app.main.ui.dashboard.team-form]
|
[app.main.ui.dashboard.team-form]
|
||||||
[app.main.ui.icons :as i :refer [icon-xref]]
|
[app.main.ui.icons :as i :refer [icon-xref]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
@ -333,10 +333,10 @@
|
||||||
:alt (:name team-item)}]
|
:alt (:name team-item)}]
|
||||||
|
|
||||||
(if (and (contains? cf/flags :subscriptions)
|
(if (and (contains? cf/flags :subscriptions)
|
||||||
(or (= "unlimited" (:type (:subscription team-item))) (= "enterprise" (:type (:subscription team-item)))))
|
(#{"unlimited" "enterprise"} (get-subscription-type (:subscription team-item))))
|
||||||
[:div {:class (stl/css :team-text-with-icon)}
|
[:div {:class (stl/css :team-text-with-icon)}
|
||||||
[:span {:class (stl/css :team-text) :title (:name team-item)} (:name team-item)]
|
[:span {:class (stl/css :team-text) :title (:name team-item)} (:name team-item)]
|
||||||
[:> menu-team-icon* {:subscription-name (:type (:subscription team-item))}]]
|
[:> menu-team-icon* {:subscription-type (get-subscription-type (:subscription team-item))}]]
|
||||||
[:span {:class (stl/css :team-text)
|
[:span {:class (stl/css :team-text)
|
||||||
:title (:name team-item)} (:name team-item)])
|
:title (:name team-item)} (:name team-item)])
|
||||||
(when (= (:id team-item) (:id team))
|
(when (= (:id team-item) (:id team))
|
||||||
|
@ -654,7 +654,7 @@
|
||||||
(fn []
|
(fn []
|
||||||
(reset! show-teams-ddwn? false))
|
(reset! show-teams-ddwn? false))
|
||||||
subscription (:subscription team)
|
subscription (:subscription team)
|
||||||
subscription-name (:type subscription)]
|
subscription-type (get-subscription-type subscription)]
|
||||||
|
|
||||||
[:div {:class (stl/css :sidebar-team-switch)}
|
[:div {:class (stl/css :sidebar-team-switch)}
|
||||||
[:div {:class (stl/css :switch-content)}
|
[:div {:class (stl/css :switch-content)}
|
||||||
|
@ -669,18 +669,18 @@
|
||||||
|
|
||||||
(and (contains? cf/flags :subscriptions)
|
(and (contains? cf/flags :subscriptions)
|
||||||
(not (:is-default team))
|
(not (:is-default team))
|
||||||
(or (= "unlimited" subscription-name) (= "enterprise" subscription-name)))
|
(or (= "unlimited" subscription-type) (= "enterprise" subscription-type)))
|
||||||
[:div {:class (stl/css :team-name)}
|
[:div {:class (stl/css :team-name)}
|
||||||
[:img {:src (cf/resolve-team-photo-url team)
|
[:img {:src (cf/resolve-team-photo-url team)
|
||||||
:class (stl/css :team-picture)
|
:class (stl/css :team-picture)
|
||||||
:alt (:name team)}]
|
:alt (:name team)}]
|
||||||
[:div {:class (stl/css :team-text-with-icon)}
|
[:div {:class (stl/css :team-text-with-icon)}
|
||||||
[:span {:class (stl/css :team-text) :title (:name team)} (:name team)]
|
[:span {:class (stl/css :team-text) :title (:name team)} (:name team)]
|
||||||
[:> menu-team-icon* {:subscription-name subscription-name}]]]
|
[:> menu-team-icon* {:subscription-type subscription-type}]]]
|
||||||
|
|
||||||
|
|
||||||
(and (not (:is-default team))
|
(and (not (:is-default team))
|
||||||
(or (not= "unlimited" subscription-name) (not= "enterprise" subscription-name)))
|
(or (not= "unlimited" subscription-type) (not= "enterprise" subscription-type)))
|
||||||
[:div {:class (stl/css :team-name)}
|
[:div {:class (stl/css :team-name)}
|
||||||
[:img {:src (cf/resolve-team-photo-url team)
|
[:img {:src (cf/resolve-team-photo-url team)
|
||||||
:class (stl/css :team-picture)
|
:class (stl/css :team-picture)
|
||||||
|
|
|
@ -18,6 +18,12 @@
|
||||||
[potok.v2.core :as ptk]
|
[potok.v2.core :as ptk]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(defn get-subscription-type
|
||||||
|
[{:keys [type status] :as subscription}]
|
||||||
|
(if (and subscription (not (contains? #{"unpaid" "canceled"} status)))
|
||||||
|
type
|
||||||
|
"professional"))
|
||||||
|
|
||||||
(mf/defc cta-power-up*
|
(mf/defc cta-power-up*
|
||||||
[{:keys [top-title top-description bottom-description has-dropdown]}]
|
[{:keys [top-title top-description bottom-description has-dropdown]}]
|
||||||
(let [show-data* (mf/use-state false)
|
(let [show-data* (mf/use-state false)
|
||||||
|
@ -46,13 +52,11 @@
|
||||||
(mf/defc subscription-sidebar*
|
(mf/defc subscription-sidebar*
|
||||||
[{:keys [profile]}]
|
[{:keys [profile]}]
|
||||||
(let [subscription (:subscription (:props profile))
|
(let [subscription (:subscription (:props profile))
|
||||||
subscription-name (if subscription
|
subscription-type (get-subscription-type subscription)
|
||||||
(:type subscription)
|
|
||||||
"professional")
|
|
||||||
subscription-is-trial (= (:status subscription) "trialing")
|
subscription-is-trial (= (:status subscription) "trialing")
|
||||||
subscription-href (dm/str (u/join cf/public-uri "#/settings/subscriptions"))]
|
subscription-href (dm/str (u/join cf/public-uri "#/settings/subscriptions"))]
|
||||||
|
|
||||||
(case subscription-name
|
(case subscription-type
|
||||||
"professional"
|
"professional"
|
||||||
[:> cta-power-up*
|
[:> cta-power-up*
|
||||||
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
|
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
|
||||||
|
@ -88,7 +92,7 @@
|
||||||
(mf/defc team*
|
(mf/defc team*
|
||||||
[{:keys [is-owner team]}]
|
[{:keys [is-owner team]}]
|
||||||
(let [subscription (:subscription team)
|
(let [subscription (:subscription team)
|
||||||
subscription-name (:type subscription)
|
subscription-type (get-subscription-type subscription)
|
||||||
subscription-is-trial (= "trialing" (:status subscription))
|
subscription-is-trial (= "trialing" (:status subscription))
|
||||||
|
|
||||||
go-to-manage-subscription
|
go-to-manage-subscription
|
||||||
|
@ -106,23 +110,23 @@
|
||||||
[:div {:class (stl/css :team-label)}
|
[:div {:class (stl/css :team-label)}
|
||||||
(tr "subscription.dashboard.team-plan")]
|
(tr "subscription.dashboard.team-plan")]
|
||||||
[:span {:class (stl/css :team-text)}
|
[:span {:class (stl/css :team-text)}
|
||||||
(case subscription-name
|
(case subscription-type
|
||||||
"professional" (tr "subscription.settings.professional")
|
"professional" (tr "subscription.settings.professional")
|
||||||
"unlimited" (if subscription-is-trial
|
"unlimited" (if subscription-is-trial
|
||||||
(tr "subscription.settings.unlimited-trial")
|
(tr "subscription.settings.unlimited-trial")
|
||||||
(tr "subscription.settings.unlimited"))
|
(tr "subscription.settings.unlimited"))
|
||||||
|
|
||||||
"enterprise" (tr "subscription.settings.enterprise"))]
|
"enterprise" (tr "subscription.settings.enterprise"))]
|
||||||
(when (and is-owner (not= subscription-name "professional"))
|
(when (and is-owner (not= subscription-type "professional"))
|
||||||
[:button {:class (stl/css :manage-subscription-link)
|
[:button {:class (stl/css :manage-subscription-link)
|
||||||
:on-click go-to-manage-subscription
|
:on-click go-to-manage-subscription
|
||||||
:data-testid "manage-subscription-link"}
|
:data-testid "manage-subscription-link"}
|
||||||
(tr "subscription.settings.manage-your-subscription")])]))
|
(tr "subscription.settings.manage-your-subscription")])]))
|
||||||
|
|
||||||
(mf/defc menu-team-icon*
|
(mf/defc menu-team-icon*
|
||||||
[{:keys [subscription-name]}]
|
[{:keys [subscription-type]}]
|
||||||
[:span {:class (stl/css :subscription-icon) :data-testid "subscription-icon"}
|
[:span {:class (stl/css :subscription-icon) :data-testid "subscription-icon"}
|
||||||
(case subscription-name
|
(case subscription-type
|
||||||
"unlimited" i/character-u
|
"unlimited" i/character-u
|
||||||
"enterprise" i/character-e)])
|
"enterprise" i/character-e)])
|
||||||
|
|
||||||
|
@ -139,15 +143,15 @@
|
||||||
[:span {:class (stl/css :item-name)} (tr "subscription.workspace.header.menu.option.power-up")]]))
|
[:span {:class (stl/css :item-name)} (tr "subscription.workspace.header.menu.option.power-up")]]))
|
||||||
|
|
||||||
(mf/defc members-cta*
|
(mf/defc members-cta*
|
||||||
[{:keys [banner-is-expanded team profile]}]
|
[{:keys [banner-is-expanded team]}]
|
||||||
(let [subscription (:subscription team)
|
(let [subscription (:subscription team)
|
||||||
subscription-name (:type subscription)
|
subscription-type (get-subscription-type subscription)
|
||||||
is-owner (:is-owner (:permissions team))
|
is-owner (-> team :permissions :is-owner)
|
||||||
|
|
||||||
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
|
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
|
||||||
mail-to-owner (str "<a href=\"" "mailto:" email-owner "\">" email-owner "</a>")
|
mail-to-owner (str "<a href=\"" "mailto:" email-owner "\">" email-owner "</a>")
|
||||||
go-to-subscription (dm/str (u/join cf/public-uri "#/settings/subscriptions"))
|
go-to-subscription (dm/str (u/join cf/public-uri "#/settings/subscriptions"))
|
||||||
seats (-> profile :props :subscription :quantity)
|
seats (or (:seats subscription) 0)
|
||||||
editors (count (filterv :can-edit (:members team)))
|
editors (count (filterv :can-edit (:members team)))
|
||||||
|
|
||||||
link
|
link
|
||||||
|
@ -157,24 +161,24 @@
|
||||||
|
|
||||||
cta-title
|
cta-title
|
||||||
(cond
|
(cond
|
||||||
(and (= "professional" subscription-name) (>= editors 8))
|
(and (= "professional" subscription-type) (>= editors 8))
|
||||||
(tr "subscription.dashboard.cta.professional-plan-designed")
|
(tr "subscription.dashboard.cta.professional-plan-designed")
|
||||||
|
|
||||||
(and (= "unlimited" subscription-name) (< seats editors))
|
(and (= "unlimited" subscription-type) (< seats editors))
|
||||||
(tr "subscription.dashboard.cta.unlimited-many-editors" seats editors))
|
(tr "subscription.dashboard.cta.unlimited-many-editors" seats editors))
|
||||||
|
|
||||||
cta-message
|
cta-message
|
||||||
(cond
|
(cond
|
||||||
(and (= "professional" subscription-name) (>= editors 8) is-owner)
|
(and (= "professional" subscription-type) (>= editors 8) is-owner)
|
||||||
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner" link)
|
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner" link)
|
||||||
|
|
||||||
(and (= "professional" subscription-name) (>= editors 8) (not is-owner))
|
(and (= "professional" subscription-type) (>= editors 8) (not is-owner))
|
||||||
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-member" link)
|
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-member" link)
|
||||||
|
|
||||||
(and (= "unlimited" subscription-name) (< seats editors) is-owner)
|
(and (= "unlimited" subscription-type) (< seats editors) is-owner)
|
||||||
(tr "subscription.dashboard.cta.upgrade-more-seats-owner" link)
|
(tr "subscription.dashboard.cta.upgrade-more-seats-owner" link)
|
||||||
|
|
||||||
(and (= "unlimited" subscription-name) (< seats editors) (not is-owner))
|
(and (= "unlimited" subscription-type) (< seats editors) (not is-owner))
|
||||||
(tr "subscription.dashboard.cta.upgrade-more-seats-member" link))]
|
(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}
|
[:> cta* {:class (stl/css-case ::members-cta-full-width banner-is-expanded :members-cta (not banner-is-expanded)) :title cta-title}
|
||||||
|
@ -184,25 +188,26 @@
|
||||||
:content cta-message}]]))
|
:content cta-message}]]))
|
||||||
|
|
||||||
(defn show-subscription-members-main-banner?
|
(defn show-subscription-members-main-banner?
|
||||||
[team profile]
|
[team]
|
||||||
(let [seats (-> profile :props :subscription :quantity)
|
(let [subscription-type (get-subscription-type (:subscription team))
|
||||||
editors (count (filter :can-edit (:members team)))]
|
seats (-> team :subscription :seats)
|
||||||
|
editors (count (filter :can-edit (:members team)))]
|
||||||
(or
|
(or
|
||||||
(and (= (:type (:subscription team)) "professional")
|
(and (= subscription-type "professional")
|
||||||
(> editors 8))
|
(> editors 8))
|
||||||
(and
|
(and
|
||||||
(= (:type (:subscription team)) "unlimited")
|
(= subscription-type "unlimited")
|
||||||
(>= editors 8)
|
(>= editors 8)
|
||||||
(< seats editors)))))
|
(< seats editors)))))
|
||||||
|
|
||||||
(defn show-subscription-members-small-banner?
|
(defn show-subscription-members-small-banner?
|
||||||
[team profile]
|
[team]
|
||||||
(let [seats (-> profile :props :subscription :quantity)
|
(let [subscription-type (get-subscription-type (:subscription team))
|
||||||
|
seats (-> team :subscription :seats)
|
||||||
editors (count (filterv :can-edit (:members team)))]
|
editors (count (filterv :can-edit (:members team)))]
|
||||||
(or
|
(or
|
||||||
(and (= (:type (:subscription team)) "professional")
|
(and (= subscription-type "professional")
|
||||||
(= editors 8))
|
(= editors 8))
|
||||||
(and (= (:type (:subscription team)) "unlimited")
|
(and (= subscription-type "unlimited")
|
||||||
(< editors 8)
|
(< editors 8)
|
||||||
(< seats editors)))))
|
(< seats editors)))))
|
||||||
|
|
||||||
|
|
|
@ -544,18 +544,18 @@
|
||||||
[:section {:class (stl/css-case
|
[:section {:class (stl/css-case
|
||||||
:dashboard-container true
|
:dashboard-container true
|
||||||
:dashboard-team-members true
|
:dashboard-team-members true
|
||||||
:dashboard-top-cta (show-subscription-members-main-banner? team profile))}
|
:dashboard-top-cta (show-subscription-members-main-banner? team))}
|
||||||
(when (and (contains? cfg/flags :subscriptions)
|
(when (and (contains? cfg/flags :subscriptions)
|
||||||
(show-subscription-members-main-banner? team profile))
|
(show-subscription-members-main-banner? team))
|
||||||
[:> members-cta* {:banner-is-expanded true :team team :profile profile}])
|
[:> members-cta* {:banner-is-expanded true :team team}])
|
||||||
[:> team-members*
|
[:> team-members*
|
||||||
{:profile profile
|
{:profile profile
|
||||||
:team team}]
|
:team team}]
|
||||||
|
|
||||||
(when (and
|
(when (and
|
||||||
(contains? cfg/flags :subscriptions)
|
(contains? cfg/flags :subscriptions)
|
||||||
(show-subscription-members-small-banner? team profile))
|
(show-subscription-members-small-banner? team))
|
||||||
[:> members-cta* {:banner-is-expanded false :team team :profile profile}])]])
|
[:> members-cta* {:banner-is-expanded false :team team}])]])
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; INVITATIONS SECTION
|
;; INVITATIONS SECTION
|
||||||
|
@ -803,7 +803,7 @@
|
||||||
|
|
||||||
(mf/defc team-invitations-page*
|
(mf/defc team-invitations-page*
|
||||||
{::mf/props :obj}
|
{::mf/props :obj}
|
||||||
[{:keys [team profile]}]
|
[{:keys [team]}]
|
||||||
|
|
||||||
(mf/with-effect [team]
|
(mf/with-effect [team]
|
||||||
(dom/set-html-title
|
(dom/set-html-title
|
||||||
|
@ -820,14 +820,14 @@
|
||||||
:team team}]
|
:team team}]
|
||||||
[:section {:class (stl/css-case
|
[:section {:class (stl/css-case
|
||||||
:dashboard-team-invitations true
|
:dashboard-team-invitations true
|
||||||
:dashboard-top-cta (show-subscription-members-main-banner? team profile))}
|
:dashboard-top-cta (show-subscription-members-main-banner? team))}
|
||||||
(when (and (contains? cfg/flags :subscriptions)
|
(when (and (contains? cfg/flags :subscriptions)
|
||||||
(show-subscription-members-main-banner? team profile))
|
(show-subscription-members-main-banner? team))
|
||||||
[:> members-cta* {:banner-is-expanded true :team team :profile profile}])
|
[:> members-cta* {:banner-is-expanded true :team team}])
|
||||||
[:> invitation-section* {:team team}]
|
[:> invitation-section* {:team team}]
|
||||||
(when (and (contains? cfg/flags :subscriptions)
|
(when (and (contains? cfg/flags :subscriptions)
|
||||||
(show-subscription-members-small-banner? team profile))
|
(show-subscription-members-small-banner? team))
|
||||||
[:> members-cta* {:banner-is-expanded false :team team :profile profile}])]])
|
[:> members-cta* {:banner-is-expanded false :team team}])]])
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; WEBHOOKS SECTION
|
;; WEBHOOKS SECTION
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.math :as mth]
|
[app.common.schema :as sm]
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.profile :as du]
|
[app.main.data.profile :as du]
|
||||||
|
@ -10,6 +10,8 @@
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.router :as rt]
|
[app.main.router :as rt]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.components.forms :as fm]
|
||||||
|
[app.main.ui.dashboard.subscription :refer [get-subscription-type]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
@ -52,42 +54,45 @@
|
||||||
:on-click cta-link} cta-text])
|
:on-click cta-link} cta-text])
|
||||||
(when (and cta-link-trial cta-text-trial) [:button {:class (stl/css :cta-button :bottom-link)
|
(when (and cta-link-trial cta-text-trial) [:button {:class (stl/css :cta-button :bottom-link)
|
||||||
:on-click cta-link-trial} cta-text-trial])])
|
:on-click cta-link-trial} cta-text-trial])])
|
||||||
|
(def ^:private schema:seats-form
|
||||||
|
[:map {:title "SeatsForm"}
|
||||||
|
[:min-members [::sm/number {:min 1 :max 9999}]]])
|
||||||
|
|
||||||
(mf/defc subscribe-management-dialog
|
(mf/defc subscribe-management-dialog
|
||||||
{::mf/register modal/components
|
{::mf/register modal/components
|
||||||
::mf/register-as :management-dialog}
|
::mf/register-as :management-dialog}
|
||||||
[{:keys [subscription-name teams subscribe-to-trial]}]
|
[{:keys [subscription-type teams subscribe-to-trial]}]
|
||||||
|
|
||||||
(let [min-members* (mf/use-state (or (some->> teams (map :total-editors) (apply max)) 1))
|
(let [subscription-name (if subscribe-to-trial
|
||||||
min-members (deref min-members*)
|
(if (= subscription-type "unlimited")
|
||||||
formatted-subscription-name (if subscribe-to-trial
|
(tr "subscription.settings.unlimited-trial")
|
||||||
(if (= subscription-name "unlimited")
|
(tr "subscription.settings.enterprise-trial"))
|
||||||
(tr "subscription.settings.unlimited-trial")
|
(case subscription-type
|
||||||
(tr "subscription.settings.enterprise-trial"))
|
"professional" (tr "subscription.settings.professional")
|
||||||
(case subscription-name
|
"unlimited" (tr "subscription.settings.unlimited")
|
||||||
"professional" (tr "subscription.settings.professional")
|
"enterprise" (tr "subscription.settings.enterprise")))
|
||||||
"unlimited" (tr "subscription.settings.unlimited")
|
initial (mf/with-memo []
|
||||||
"enterprise" (tr "subscription.settings.enterprise")))
|
{:min-members (or (some->> teams (map :total-editors) (apply max)) 1)})
|
||||||
handle-subscription-trial (if (= subscription-name "unlimited")
|
form (fm/use-form :schema schema:seats-form
|
||||||
(mf/use-fn
|
:initial initial)
|
||||||
(mf/deps min-members)
|
subscribe-to-unlimited (mf/use-fn
|
||||||
(fn []
|
(fn [form]
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "create-trial-subscription"
|
(let [data (:clean-data @form)
|
||||||
:type "unlimited"
|
return-url (-> (rt/get-current-href) (rt/encode-url))
|
||||||
:quantity min-members}))
|
href (dm/str "payments/subscriptions/create?type=unlimited&quantity=" (:min-members data) "&returnUrl=" return-url)]
|
||||||
(let [current-href (rt/get-current-href)
|
(st/emit! (ptk/event ::ev/event {::ev/name "create-trial-subscription"
|
||||||
returnUrl (js/encodeURIComponent current-href)
|
:type "unlimited"
|
||||||
href (dm/str "payments/subscriptions/create?type=unlimited&quantity=" min-members "&returnUrl=" returnUrl)]
|
:quantity (:min-members data)})
|
||||||
(st/emit! (rt/nav-raw :href href)))))
|
(rt/nav-raw :href href)))))
|
||||||
|
|
||||||
|
subscribe-to-enterprise (mf/use-fn
|
||||||
|
(fn []
|
||||||
|
(st/emit! (ptk/event ::ev/event {::ev/name "create-trial-subscription"
|
||||||
|
:type "enterprise"}))
|
||||||
|
(let [return-url (-> (rt/get-current-href) (rt/encode-url))
|
||||||
|
href (dm/str "payments/subscriptions/create?type=enterprise&returnUrl=" return-url)]
|
||||||
|
(st/emit! (rt/nav-raw :href href)))))
|
||||||
|
|
||||||
(mf/use-fn
|
|
||||||
(fn []
|
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "create-trial-subscription"
|
|
||||||
:type "enterprise"}))
|
|
||||||
(let [current-href (rt/get-current-href)
|
|
||||||
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-fn
|
handle-accept-dialog (mf/use-fn
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "open-subscription-management"
|
(st/emit! (ptk/event ::ev/event {::ev/name "open-subscription-management"
|
||||||
|
@ -107,62 +112,71 @@
|
||||||
[:div {:class (stl/css :modal-dialog)}
|
[:div {:class (stl/css :modal-dialog)}
|
||||||
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} i/close]
|
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} i/close]
|
||||||
[:div {:class (stl/css :modal-title :subscription-title)}
|
[:div {:class (stl/css :modal-title :subscription-title)}
|
||||||
(tr "subscription.settings.management.dialog.title" formatted-subscription-name)]
|
(tr "subscription.settings.management.dialog.title" subscription-name)]
|
||||||
|
|
||||||
[:div {:class (stl/css :modal-content)}
|
[:div {:class (stl/css :modal-content)}
|
||||||
(if (seq teams)
|
(if (seq teams)
|
||||||
[* [:div {:class (stl/css :modal-text)}
|
[:* [:div {:class (stl/css :modal-text)}
|
||||||
(tr "subscription.settings.management.dialog.choose-this-plan")]
|
(tr "subscription.settings.management.dialog.choose-this-plan")]
|
||||||
[:ul {:class (stl/css :teams-list)}
|
[:ul {:class (stl/css :teams-list)}
|
||||||
(for [team (js->clj teams :keywordize-keys true)]
|
(for [team teams]
|
||||||
[:li {:key (dm/str (:id team)) :class (stl/css :team-name)}
|
[:li {:key (dm/str (:id team)) :class (stl/css :team-name)}
|
||||||
(:name team) (tr "subscription.settings.management.dialog.members" (:total-editors team))])]]
|
(:name team) (tr "subscription.settings.management.dialog.members" (:total-editors team))])]]
|
||||||
[:div {:class (stl/css :modal-text)}
|
[:div {:class (stl/css :modal-text)}
|
||||||
(tr "subscription.settings.management.dialog.no-teams")])
|
(tr "subscription.settings.management.dialog.no-teams")])
|
||||||
|
|
||||||
(when (and (= subscription-name "unlimited") subscribe-to-trial)
|
|
||||||
[[:label {:for "editors-subscription" :class (stl/css :modal-text :editors-label)}
|
|
||||||
(tr "subscription.settings.management.dialog.select-editors")]
|
|
||||||
[:div {:class (stl/css :editors-wrapper)}
|
|
||||||
[:div {:class (stl/css :input-wrapper)}
|
|
||||||
[:input {:id "editors-subscription"
|
|
||||||
:class (stl/css :input-field)
|
|
||||||
:type "number"
|
|
||||||
:value min-members
|
|
||||||
:min 1
|
|
||||||
:max 1000
|
|
||||||
:on-change #(let [new-value (js/parseInt (.. % -target -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)]
|
|
||||||
[:span {:class (stl/css :modal-text-small)}
|
|
||||||
(tr "subscription.settings.management.dialog.payment-explanation")]]]])
|
|
||||||
|
|
||||||
(when (and
|
(when (and
|
||||||
(or (= subscription-name "professional") (= subscription-name "unlimited"))
|
(or (= subscription-type "professional") (= subscription-type "unlimited"))
|
||||||
(not subscribe-to-trial))
|
(not subscribe-to-trial))
|
||||||
[:div {:class (stl/css :modal-text)}
|
[:div {:class (stl/css :modal-text)}
|
||||||
(tr "subscription.settings.management.dialog.downgrade")])
|
(tr "subscription.settings.management.dialog.downgrade")])
|
||||||
|
|
||||||
[:div {:class (stl/css :modal-footer)}
|
(if (and (= subscription-type "unlimited") subscribe-to-trial)
|
||||||
[:div {:class (stl/css :action-buttons)}
|
[:& fm/form {:on-submit subscribe-to-unlimited
|
||||||
[:input
|
:class (stl/css :seats-form)
|
||||||
{:class (stl/css :cancel-button)
|
:form form}
|
||||||
:type "button"
|
[:label {:for "editors-subscription" :class (stl/css :modal-text :editors-label)}
|
||||||
:value (tr "ds.confirm-cancel")
|
(tr "subscription.settings.management.dialog.select-editors")]
|
||||||
:on-click handle-close-dialog}]
|
|
||||||
|
|
||||||
[:input
|
[:div {:class (stl/css :editors-wrapper)}
|
||||||
{:class (stl/css :primary-button)
|
[:div {:class (stl/css :fields-row)}
|
||||||
:type "button"
|
[:& fm/input {:type "number"
|
||||||
:value (if subscribe-to-trial (tr "subscription.settings.start-trial") (tr "labels.continue"))
|
:name :min-members
|
||||||
:on-click (if subscribe-to-trial handle-subscription-trial handle-accept-dialog)}]]]]]]))
|
:show-error false
|
||||||
|
:label ""
|
||||||
|
:class (stl/css :input-field)}]]
|
||||||
|
[:div {:class (stl/css :editors-cost)}
|
||||||
|
[:span {:class (stl/css :modal-text-small)}
|
||||||
|
(tr "subscription.settings.management.dialog.price-month" (or (get-in @form [:clean-data :min-members]) 0))]
|
||||||
|
[:span {:class (stl/css :modal-text-small)}
|
||||||
|
(tr "subscription.settings.management.dialog.payment-explanation")]]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-footer)}
|
||||||
|
[:div {:class (stl/css :action-buttons)}
|
||||||
|
[:input
|
||||||
|
{:class (stl/css :cancel-button)
|
||||||
|
:type "button"
|
||||||
|
:value (tr "ds.confirm-cancel")
|
||||||
|
:on-click handle-close-dialog}]
|
||||||
|
|
||||||
|
[:> fm/submit-button*
|
||||||
|
{:label (tr "subscription.settings.start-trial")
|
||||||
|
:class (stl/css :primary-button)}]]]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-footer)}
|
||||||
|
[:div {:class (stl/css :action-buttons)}
|
||||||
|
[:input
|
||||||
|
{:class (stl/css :cancel-button)
|
||||||
|
:type "button"
|
||||||
|
:value (tr "ds.confirm-cancel")
|
||||||
|
:on-click handle-close-dialog}]
|
||||||
|
|
||||||
|
[:input
|
||||||
|
{:class (stl/css :primary-button)
|
||||||
|
|
||||||
|
:type "button"
|
||||||
|
:value (if subscribe-to-trial (tr "subscription.settings.start-trial") (tr "labels.continue"))
|
||||||
|
:on-click (if subscribe-to-trial subscribe-to-enterprise handle-accept-dialog)}]]])]]]))
|
||||||
|
|
||||||
(mf/defc subscription-success-dialog
|
(mf/defc subscription-success-dialog
|
||||||
{::mf/register modal/components
|
{::mf/register modal/components
|
||||||
|
@ -201,13 +215,12 @@
|
||||||
(let [route (mf/deref refs/route)
|
(let [route (mf/deref refs/route)
|
||||||
params (:params route)
|
params (:params route)
|
||||||
params-subscription (:subscription (:query params))
|
params-subscription (:subscription (:query params))
|
||||||
show-subscription-success-modal (and (:query params)
|
show-trial-subscription-modal (or (= params-subscription "subscription-to-penpot-unlimited")
|
||||||
(or (= (:subscription (:query params)) "subscribed-to-penpot-unlimited")
|
(= params-subscription "subscription-to-penpot-enterprise"))
|
||||||
(= (:subscription (:query params)) "subscribed-to-penpot-enterprise")))
|
show-subscription-success-modal (or (= params-subscription "subscribed-to-penpot-unlimited")
|
||||||
|
(= params-subscription "subscribed-to-penpot-enterprise"))
|
||||||
subscription (:subscription (:props profile))
|
subscription (:subscription (:props profile))
|
||||||
subscription-name (if subscription
|
subscription-type (get-subscription-type subscription)
|
||||||
(:type subscription)
|
|
||||||
"professional")
|
|
||||||
subscription-is-trial (= (:status subscription) "trialing")
|
subscription-is-trial (= (:status subscription) "trialing")
|
||||||
teams* (mf/use-state nil)
|
teams* (mf/use-state nil)
|
||||||
teams (deref teams*)
|
teams (deref teams*)
|
||||||
|
@ -229,11 +242,12 @@
|
||||||
(st/emit! (rt/nav-raw :href href)))))
|
(st/emit! (rt/nav-raw :href href)))))
|
||||||
open-subscription-modal (mf/use-fn
|
open-subscription-modal (mf/use-fn
|
||||||
(mf/deps teams)
|
(mf/deps teams)
|
||||||
(fn [subscription-name]
|
(fn [subscription-type]
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "open-subscription-modal"}))
|
(st/emit! (ptk/event ::ev/event {::ev/name "open-subscription-modal"
|
||||||
|
::ev/origin "settings:in-app"}))
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(modal/show :management-dialog
|
(modal/show :management-dialog
|
||||||
{:subscription-name subscription-name
|
{:subscription-type subscription-type
|
||||||
:teams teams :subscribe-to-trial (not subscription)}))))]
|
:teams teams :subscribe-to-trial (not subscription)}))))]
|
||||||
|
|
||||||
(mf/with-effect []
|
(mf/with-effect []
|
||||||
|
@ -244,6 +258,19 @@
|
||||||
(mf/with-effect []
|
(mf/with-effect []
|
||||||
(dom/set-html-title (tr "subscription.labels")))
|
(dom/set-html-title (tr "subscription.labels")))
|
||||||
|
|
||||||
|
(mf/with-effect [show-trial-subscription-modal subscription]
|
||||||
|
(when show-trial-subscription-modal
|
||||||
|
(st/emit!
|
||||||
|
(ptk/event ::ev/event {::ev/name "open-subscription-modal"
|
||||||
|
::ev/origin "settings:from-pricing-page"})
|
||||||
|
(modal/show :management-dialog
|
||||||
|
{:subscription-type (if (= params-subscription "subscription-to-penpot-unlimited")
|
||||||
|
"unlimited"
|
||||||
|
"enterprise")
|
||||||
|
:teams teams
|
||||||
|
:subscribe-to-trial (not subscription)})
|
||||||
|
(rt/nav :settings-subscription {} {::rt/replace true}))))
|
||||||
|
|
||||||
(mf/with-effect [show-subscription-success-modal subscription]
|
(mf/with-effect [show-subscription-success-modal subscription]
|
||||||
(when show-subscription-success-modal
|
(when show-subscription-success-modal
|
||||||
(st/emit!
|
(st/emit!
|
||||||
|
@ -266,7 +293,7 @@
|
||||||
|
|
||||||
[:div {:class (stl/css :your-subscription)}
|
[:div {:class (stl/css :your-subscription)}
|
||||||
[:h3 {:class (stl/css :plan-section-title)} (tr "subscription.settings.section-plan")]
|
[:h3 {:class (stl/css :plan-section-title)} (tr "subscription.settings.section-plan")]
|
||||||
(case subscription-name
|
(case subscription-type
|
||||||
"professional"
|
"professional"
|
||||||
[:> plan-card* {:card-title (tr "subscription.settings.professional")
|
[:> plan-card* {:card-title (tr "subscription.settings.professional")
|
||||||
:benefits [(tr "subscription.settings.professional.projects-files"),
|
:benefits [(tr "subscription.settings.professional.projects-files"),
|
||||||
|
@ -327,7 +354,7 @@
|
||||||
|
|
||||||
[:div {:class (stl/css :other-subscriptions)}
|
[:div {:class (stl/css :other-subscriptions)}
|
||||||
[:h3 {:class (stl/css :plan-section-title)} (tr "subscription.settings.other-plans")]
|
[:h3 {:class (stl/css :plan-section-title)} (tr "subscription.settings.other-plans")]
|
||||||
(when (not= subscription-name "professional")
|
(when (not= subscription-type "professional")
|
||||||
[:> plan-card* {:card-title (tr "subscription.settings.professional")
|
[:> plan-card* {:card-title (tr "subscription.settings.professional")
|
||||||
:price-value "$0"
|
:price-value "$0"
|
||||||
:price-period (tr "subscription.settings.price-editor-month")
|
:price-period (tr "subscription.settings.price-editor-month")
|
||||||
|
@ -339,7 +366,7 @@
|
||||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||||
:cta-link-with-icon go-to-pricing-page}])
|
:cta-link-with-icon go-to-pricing-page}])
|
||||||
|
|
||||||
(when (not= subscription-name "unlimited")
|
(when (not= subscription-type "unlimited")
|
||||||
[:> plan-card* {:card-title (tr "subscription.settings.unlimited")
|
[:> plan-card* {:card-title (tr "subscription.settings.unlimited")
|
||||||
:card-title-icon i/character-u
|
:card-title-icon i/character-u
|
||||||
:price-value "$7"
|
:price-value "$7"
|
||||||
|
@ -353,7 +380,7 @@
|
||||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||||
:cta-link-with-icon go-to-pricing-page}])
|
:cta-link-with-icon go-to-pricing-page}])
|
||||||
|
|
||||||
(when (not= subscription-name "enterprise")
|
(when (not= subscription-type "enterprise")
|
||||||
[:> plan-card* {:card-title (tr "subscription.settings.enterprise")
|
[:> plan-card* {:card-title (tr "subscription.settings.enterprise")
|
||||||
:card-title-icon i/character-e
|
:card-title-icon i/character-e
|
||||||
:price-value "$950"
|
:price-value "$950"
|
||||||
|
|
|
@ -253,8 +253,8 @@
|
||||||
margin-block: var(--sp-xxl);
|
margin-block: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-wrapper {
|
.input-field {
|
||||||
@extend .input-element;
|
--input-icon-padding: var(--sp-s);
|
||||||
width: $s-80;
|
width: $s-80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.dashboard.subscription :refer [main-menu-power-up*]]
|
[app.main.ui.dashboard.subscription :refer [main-menu-power-up* get-subscription-type]]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
[app.main.ui.hooks.resize :as r]
|
[app.main.ui.hooks.resize :as r]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
|
@ -825,9 +825,7 @@
|
||||||
(modal/show :plugin-management {}))))
|
(modal/show :plugin-management {}))))
|
||||||
|
|
||||||
subscription (:subscription (:props profile))
|
subscription (:subscription (:props profile))
|
||||||
subscription-name (if subscription
|
subscription-type (get-subscription-type subscription)]
|
||||||
(:type subscription)
|
|
||||||
"professional")]
|
|
||||||
|
|
||||||
(mf/with-effect []
|
(mf/with-effect []
|
||||||
(let [disposable (->> st/stream
|
(let [disposable (->> st/stream
|
||||||
|
@ -913,7 +911,7 @@
|
||||||
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.help-info")]
|
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.help-info")]
|
||||||
[:span {:class (stl/css :open-arrow)} i/arrow]]
|
[:span {:class (stl/css :open-arrow)} i/arrow]]
|
||||||
|
|
||||||
(when (and (contains? cf/flags :subscriptions) (not= "enterprise" subscription-name))
|
(when (and (contains? cf/flags :subscriptions) (not= "enterprise" subscription-type))
|
||||||
[:> main-menu-power-up* {:close-sub-menu close-sub-menu}])
|
[:> main-menu-power-up* {:close-sub-menu close-sub-menu}])
|
||||||
|
|
||||||
;; TODO remove this block when subscriptions is full implemented
|
;; TODO remove this block when subscriptions is full implemented
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
[app.main.ui.components.select :refer [select]]
|
[app.main.ui.components.select :refer [select]]
|
||||||
|
[app.main.ui.dashboard.subscription :refer [get-subscription-type]]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.ds.product.autosaved-milestone :refer [autosaved-milestone*]]
|
[app.main.ui.ds.product.autosaved-milestone :refer [autosaved-milestone*]]
|
||||||
|
@ -36,26 +37,27 @@
|
||||||
|
|
||||||
(defn get-versions-stored-days
|
(defn get-versions-stored-days
|
||||||
[team]
|
[team]
|
||||||
(let [subscription-name (-> team :subscription :type)]
|
(let [subscription-type (get-subscription-type (:subscription team))]
|
||||||
(cond
|
(cond
|
||||||
(= subscription-name "unlimited") 30
|
(= subscription-type "unlimited") 30
|
||||||
(= subscription-name "enterprise") 90
|
(= subscription-type "enterprise") 90
|
||||||
:else 7)))
|
:else 7)))
|
||||||
|
|
||||||
(defn get-versions-warning-subtext
|
(defn get-versions-warning-subtext
|
||||||
[team]
|
[team]
|
||||||
(let [subscription-name (-> team :subscription :type)
|
(let [subscription-type (get-subscription-type (:subscription team))
|
||||||
is-owner? (-> team :permissions :is-owner)
|
is-owner? (-> team :permissions :is-owner)
|
||||||
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
|
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
|
||||||
|
support-email "support@penpot.app"
|
||||||
go-to-subscription (dm/str (u/join cfg/public-uri "#/settings/subscriptions"))]
|
go-to-subscription (dm/str (u/join cfg/public-uri "#/settings/subscriptions"))]
|
||||||
|
|
||||||
(if (contains? cfg/flags :subscriptions)
|
(if (contains? cfg/flags :subscriptions)
|
||||||
(if is-owner?
|
(if is-owner?
|
||||||
(if (= "enterprise" subscription-name)
|
(if (= "enterprise" subscription-type)
|
||||||
(tr "subscription.workspace.versions.warning.enterprise.subtext-owner" "support@penpot.app")
|
(tr "subscription.workspace.versions.warning.enterprise.subtext-owner" support-email support-email)
|
||||||
(tr "subscription.workspace.versions.warning.subtext-owner" go-to-subscription))
|
(tr "subscription.workspace.versions.warning.subtext-owner" go-to-subscription))
|
||||||
(tr "subscription.workspace.versions.warning.subtext-member" email-owner email-owner))
|
(tr "subscription.workspace.versions.warning.subtext-member" email-owner email-owner))
|
||||||
(tr "workspace.versions.warning.subtext" "support@penpot.app"))))
|
(tr "workspace.versions.warning.subtext" support-email))))
|
||||||
|
|
||||||
(defn group-snapshots
|
(defn group-snapshots
|
||||||
[data]
|
[data]
|
||||||
|
|
|
@ -7883,6 +7883,7 @@ msgstr ""
|
||||||
"If you'd like to increase this limit, "
|
"If you'd like to increase this limit, "
|
||||||
"[upgrade your plan|target:self](%s)"
|
"[upgrade your plan|target:self](%s)"
|
||||||
|
|
||||||
|
#, markdown
|
||||||
msgid "subscription.workspace.versions.warning.enterprise.subtext-owner"
|
msgid "subscription.workspace.versions.warning.enterprise.subtext-owner"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"If you'd like to increase this limit, write to us at "
|
"If you'd like to increase this limit, write to us at "
|
||||||
|
|
|
@ -7816,6 +7816,7 @@ msgstr ""
|
||||||
"Si quieres aumentar este límite, "
|
"Si quieres aumentar este límite, "
|
||||||
"[mejora tu plan|target:self](%s)"
|
"[mejora tu plan|target:self](%s)"
|
||||||
|
|
||||||
|
#, markdown
|
||||||
msgid "subscription.workspace.versions.warning.enterprise.subtext-owner"
|
msgid "subscription.workspace.versions.warning.enterprise.subtext-owner"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Si quieres aumentar este límite, escríbenos a "
|
"Si quieres aumentar este límite, escríbenos a "
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue