mirror of
https://github.com/penpot/penpot.git
synced 2025-06-12 18:21:39 +02:00
✨ Visual indicators subscription for teams and project settings (#6546)
* ✨ Visual indicators subscription for teams and project settings * 📎 Fixes PR feedback --------- Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
5e8929e504
commit
e5bc369e56
19 changed files with 1116 additions and 155 deletions
|
@ -31,8 +31,8 @@ export PENPOT_FLAGS="\
|
|||
enable-tiered-file-data-storage \
|
||||
enable-file-validation \
|
||||
enable-file-schema-validation \
|
||||
enable-subscriptons \
|
||||
enable-subscriptons-old";
|
||||
enable-subscriptions \
|
||||
enable-subscriptions-old";
|
||||
|
||||
# Default deletion delay for devenv
|
||||
export PENPOT_DELETION_DELAY="24h"
|
||||
|
|
|
@ -24,8 +24,8 @@ export PENPOT_FLAGS="\
|
|||
enable-tiered-file-data-storage \
|
||||
enable-file-validation \
|
||||
enable-file-schema-validation \
|
||||
enable-subscriptons \
|
||||
enable-subscriptons-old ";
|
||||
enable-subscriptions \
|
||||
enable-subscriptions-old ";
|
||||
|
||||
# Default deletion delay for devenv
|
||||
export PENPOT_DELETION_DELAY="24h"
|
||||
|
|
1
frontend/resources/images/assets/logo-subscription.svg
Normal file
1
frontend/resources/images/assets/logo-subscription.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 78 KiB |
1
frontend/resources/images/icons/logo-subscription.svg
Normal file
1
frontend/resources/images/icons/logo-subscription.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 78 KiB |
|
@ -46,6 +46,10 @@
|
|||
(update [_ state]
|
||||
(assoc state :router (create routes)))))
|
||||
|
||||
(defn encode-url
|
||||
[url]
|
||||
(js/encodeURIComponent url))
|
||||
|
||||
(defn match
|
||||
"Given routing tree and current path, return match with possibly
|
||||
coerced parameters. Return nil if no match found."
|
||||
|
|
|
@ -21,12 +21,13 @@
|
|||
[app.main.refs :as refs]
|
||||
[app.main.router :as rt]
|
||||
[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.components.link :refer [link]]
|
||||
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
|
||||
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
|
||||
[app.main.ui.dashboard.project-menu :refer [project-menu*]]
|
||||
[app.main.ui.dashboard.subscription :as subscription]
|
||||
[app.main.ui.dashboard.subscription :refer [subscription-sidebar* menu-team-icon*]]
|
||||
[app.main.ui.dashboard.team-form]
|
||||
[app.main.ui.icons :as i :refer [icon-xref]]
|
||||
[app.util.dom :as dom]
|
||||
|
@ -330,8 +331,14 @@
|
|||
[:img {:src (cf/resolve-team-photo-url team-item)
|
||||
:class (stl/css :team-picture)
|
||||
:alt (:name team-item)}]
|
||||
|
||||
(if (and (contains? cf/flags :subscriptions)
|
||||
(or (= "unlimited" (:type (:subscription team-item))) (= "enterprise" (:type (:subscription team-item)))))
|
||||
[:div {:class (stl/css :team-text-with-icon)}
|
||||
[:span {:class (stl/css :team-text) :title (:name team-item)} (:name team-item)]
|
||||
[:> menu-team-icon* {:subscription-name (:type (:subscription team-item))}]]
|
||||
[: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))
|
||||
tick-icon)])
|
||||
|
||||
|
@ -645,19 +652,35 @@
|
|||
|
||||
handle-close-team
|
||||
(fn []
|
||||
(reset! show-teams-ddwn? false))]
|
||||
(reset! show-teams-ddwn? false))
|
||||
subscription (:subscription team)
|
||||
subscription-name (:type subscription)]
|
||||
|
||||
[:div {:class (stl/css :sidebar-team-switch)}
|
||||
[:div {:class (stl/css :switch-content)}
|
||||
[:button {:class (stl/css :current-team)
|
||||
:on-click handle-show-team-click
|
||||
:on-key-down handle-show-team-keydown}
|
||||
|
||||
(if (:is-default team)
|
||||
(cond
|
||||
(:is-default team)
|
||||
[:div {:class (stl/css :team-name)}
|
||||
[:span {:class (stl/css :penpot-icon)} i/logo-icon]
|
||||
[:span {:class (stl/css :team-text)} (tr "dashboard.default-team-name")]]
|
||||
|
||||
(and (contains? cf/flags :subscriptions)
|
||||
(not (:is-default team))
|
||||
(or (= "unlimited" subscription-name) (= "enterprise" subscription-name)))
|
||||
[:div {:class (stl/css :team-name)}
|
||||
[:img {:src (cf/resolve-team-photo-url team)
|
||||
:class (stl/css :team-picture)
|
||||
:alt (:name team)}]
|
||||
[:div {:class (stl/css :team-text-with-icon)}
|
||||
[:span {:class (stl/css :team-text) :title (:name team)} (:name team)]
|
||||
[:> menu-team-icon* {:subscription-name subscription-name}]]]
|
||||
|
||||
|
||||
(and (not (:is-default team))
|
||||
(not (contains? cf/flags :subscriptions)))
|
||||
[:div {:class (stl/css :team-name)}
|
||||
[:img {:src (cf/resolve-team-photo-url team)
|
||||
:class (stl/css :team-picture)
|
||||
|
@ -964,7 +987,7 @@
|
|||
|
||||
[:*
|
||||
(when (contains? cf/flags :subscriptions)
|
||||
[:> subscription/sidebar*])
|
||||
[:> subscription-sidebar* {:profile profile}])
|
||||
|
||||
;; TODO remove this block when subscriptions is full implemented
|
||||
(when (contains? cf/flags :subscriptions-old)
|
||||
|
@ -974,7 +997,7 @@
|
|||
[:span (tr "dashboard.upgrade-plan.penpot-free")]
|
||||
[:span {:class (stl/css :no-limits)} (tr "dashboard.upgrade-plan.no-limits")]]
|
||||
[:div {:class (stl/css :power-up)}
|
||||
(tr "dashboard.upgrade-plan.power-up")]])
|
||||
(tr "subscription.dashboard.upgrade-plan.power-up")]])
|
||||
|
||||
(when (and team profile)
|
||||
[:& comments-section
|
||||
|
|
|
@ -84,11 +84,18 @@
|
|||
.team-text {
|
||||
@include textEllipsis;
|
||||
@include smallTitleTipography;
|
||||
width: $s-144;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
color: var(--menu-foreground-color-hover);
|
||||
}
|
||||
|
||||
.team-text-with-icon {
|
||||
display: flex;
|
||||
gap: $s-8;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// This icon still use the old svg
|
||||
.penpot-icon {
|
||||
@include flexCenter;
|
||||
|
|
|
@ -3,15 +3,21 @@
|
|||
(ns app.main.ui.dashboard.subscription
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.config :as cf]
|
||||
[app.main.router :as rt]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu-item*]]
|
||||
[app.main.ui.ds.product.cta :refer [cta*]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[lambdaisland.uri :as u]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc cta-power-up*
|
||||
[{:keys [top-title top-description bottom-description cta-text cta-link has-dropdown]}]
|
||||
[{:keys [top-title top-description bottom-description has-dropdown]}]
|
||||
(let [show-data* (mf/use-state false)
|
||||
show-data (deref show-data*)
|
||||
handle-click
|
||||
|
@ -24,55 +30,166 @@
|
|||
:on-click handle-click}
|
||||
[:button {:class (stl/css :cta-top-section)}
|
||||
[:div {:class (stl/css :content)}
|
||||
[:span {:class (stl/css :cta-title)} top-title]
|
||||
[:span {:class (stl/css :cta-text)} top-description]]
|
||||
[:span {:class (stl/css :cta-title :cta-text)} top-title]
|
||||
[:span {:class (stl/css :cta-text-m)} top-description]]
|
||||
(when has-dropdown [:span {:class (stl/css :icon-dropdown)} i/arrow])]
|
||||
|
||||
(when (and has-dropdown show-data)
|
||||
[:div {:class (stl/css :cta-bottom-section)}
|
||||
[:> i18n/tr-html* {:content bottom-description
|
||||
:class (stl/css :content)
|
||||
:tag-name "button"}]
|
||||
[:button {:class (stl/css :cta-highlight :cta-link) :on-click cta-link}
|
||||
cta-text]])]))
|
||||
:tag-name "span"}]])]))
|
||||
|
||||
(mf/defc sidebar*
|
||||
[]
|
||||
(let [;; TODO subscription cases professional/unlimited/enterprise
|
||||
subscription-name :unlimited
|
||||
subscription-is-trial false
|
||||
|
||||
go-to-subscription
|
||||
(mf/use-fn #(st/emit! (rt/nav :settings-subscription)))]
|
||||
(mf/defc subscription-sidebar*
|
||||
[{:keys [profile]}]
|
||||
(let [subscription (:subscription (:props profile))
|
||||
subscription-name (if subscription
|
||||
(:type subscription)
|
||||
"professional")
|
||||
subscription-is-trial (= (:status subscription) "trialing")
|
||||
subscription-href (dm/str (u/join cf/public-uri "#/settings/subscriptions"))]
|
||||
|
||||
(case subscription-name
|
||||
:professional
|
||||
"professional"
|
||||
[:> cta-power-up*
|
||||
{:top-title (tr "subscription.dashboard.power-up.professional.top-title")
|
||||
:top-description (tr "dashboard.upgrade-plan.no-limits")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.professional.bottom-description")
|
||||
:cta-text (tr "dashboard.upgrade-plan.power-up")
|
||||
:cta-link go-to-subscription
|
||||
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
|
||||
:top-description (tr "subscription.dashboard.power-up.professional.top-title")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.professional.bottom-description", subscription-href)
|
||||
:has-dropdown true}]
|
||||
|
||||
:unlimited
|
||||
"unlimited"
|
||||
(if subscription-is-trial
|
||||
[:> cta-power-up*
|
||||
{:top-title (tr "subscription.dashboard.power-up.trial.top-title")
|
||||
:top-description (tr "subscription.dashboard.power-up.trial.top-description")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.trial.bottom-description")
|
||||
:cta-text (tr "subscription.dashboard.power-up.subscribe")}]
|
||||
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
|
||||
:top-description (tr "subscription.dashboard.power-up.trial.top-title")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.trial.bottom-description", subscription-href)
|
||||
:has-dropdown true}]
|
||||
|
||||
[:> cta-power-up*
|
||||
{:top-title (tr "subscription.dashboard.power-up.unlimited-plan")
|
||||
:top-description (tr "subscription.dashboard.power-up.unlimited.top-description")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.unlimited.bottom-description")
|
||||
:cta-text (tr "subscription.dashboard.power-up.unlimited.cta")
|
||||
:cta-link go-to-subscription
|
||||
{:top-title (tr "subscription.dashboard.power-up.your-subscription")
|
||||
:top-description (tr "subscription.dashboard.power-up.unlimited-plan")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.unlimited.bottom-description", subscription-href)
|
||||
:has-dropdown true}])
|
||||
|
||||
:enterprise
|
||||
"enterprise"
|
||||
[:> cta-power-up*
|
||||
{:top-title (tr "subscription.dashboard.power-up.enterprise-plan")
|
||||
:top-description (tr "subscription.dashboard.power-up.enterprise.description")
|
||||
{: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]}]
|
||||
(let [subscription (:subscription team)
|
||||
subscription-name (:type subscription)
|
||||
subscription-is-trial (= "trialing" (:status subscription))
|
||||
|
||||
go-to-manage-subscription
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
;; TODO add event tracking
|
||||
(let [href (-> (rt/get-current-href)
|
||||
(rt/encode-url))
|
||||
href (str "payments/subscriptions/show?returnUrl=" href)]
|
||||
(st/emit! (rt/nav-raw :href href)))))]
|
||||
|
||||
[:div {:class (stl/css :team)}
|
||||
[:div {:class (stl/css :team-label)}
|
||||
(tr "subscription.dashboard.team-plan")]
|
||||
[:span {:class (stl/css :team-text)}
|
||||
(case subscription-name
|
||||
"professional" (tr "subscription.settings.professional")
|
||||
"unlimited" (if subscription-is-trial
|
||||
(tr "subscription.settings.unlimited-trial")
|
||||
(tr "subscription.settings.unlimited"))
|
||||
|
||||
"enterprise" (tr "subscription.settings.enterprise"))]
|
||||
(when (and is-owner (not= subscription-name "professional"))
|
||||
[:button {:class (stl/css :manage-subscription-link)
|
||||
:on-click go-to-manage-subscription}
|
||||
(tr "subscription.settings.manage-your-subscription")])]))
|
||||
|
||||
(mf/defc menu-team-icon*
|
||||
[{:keys [subscription-name]}]
|
||||
[:span {:class (stl/css :subscription-icon)}
|
||||
(case subscription-name
|
||||
"unlimited" i/character-u
|
||||
"enterprise" i/character-e)])
|
||||
|
||||
(mf/defc main-menu-power-up*
|
||||
[{:keys [close-sub-menu]}]
|
||||
(let [go-to-subscription (mf/use-fn #(st/emit! (rt/nav :settings-subscription)))]
|
||||
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
|
||||
:on-click go-to-subscription
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(go-to-subscription)))
|
||||
:on-pointer-enter close-sub-menu
|
||||
:id "file-menu-power-up"}
|
||||
[:span {:class (stl/css :item-name)} (tr "subscription.workspace.header.menu.option.power-up")]]))
|
||||
|
||||
(mf/defc members-cta*
|
||||
[{: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-admin %) %) (:members team)))
|
||||
mail-to-owner (str "<a href=\"" "mailto:" email-owner "\">" email-owner "</a>")
|
||||
go-to-subscription (dm/str (u/join cf/public-uri "#/settings/subscriptions"))
|
||||
|
||||
link
|
||||
(if is-owner
|
||||
go-to-subscription
|
||||
mail-to-owner)
|
||||
|
||||
cta-title
|
||||
(cond
|
||||
(= "professional" subscription-name)
|
||||
(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)))))
|
||||
|
||||
cta-message
|
||||
(cond
|
||||
(and (= "professional" subscription-name) is-owner)
|
||||
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner" link)
|
||||
|
||||
(and (= "professional" subscription-name) (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 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))]
|
||||
|
||||
[:> cta* {:class (stl/css-case ::members-cta-full-width banner-is-expanded :members-cta (not banner-is-expanded)) :title cta-title}
|
||||
[:> i18n/tr-html*
|
||||
{:tag-name "span"
|
||||
:class (stl/css :cta-message)
|
||||
:content cta-message}]]))
|
||||
|
||||
(defn show-subscription-members-main-banner?
|
||||
[team profile]
|
||||
(or
|
||||
(and (= (:type (:subscription team)) "professional") (>= (count (:members team)) 8))
|
||||
(and
|
||||
(= (:type (:subscription team)) "unlimited")
|
||||
(not (= (:status (:subscription team)) "trialing"))
|
||||
(>= (count (:members team)) (:quantity (:subscription (:props profile))))
|
||||
(:is-owner (:permissions team)))
|
||||
(= (:status (:subscription team)) "paused")))
|
||||
|
||||
(defn show-subscription-invitations-main-banner?
|
||||
[team]
|
||||
(or
|
||||
(and (= (:type (:subscription team)) "professional")
|
||||
(>= (count (:members team)) 8))
|
||||
(= (:status (:subscription team)) "paused")))
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
@use "common/refactor/common-dashboard";
|
||||
@use "../ds/typography.scss" as t;
|
||||
@use "../ds/_borders.scss" as *;
|
||||
@use "../ds/spacing.scss" as *;
|
||||
|
||||
.cta-power-up {
|
||||
display: flex;
|
||||
|
@ -27,7 +28,7 @@
|
|||
.icon-dropdown {
|
||||
@include flexCenter;
|
||||
height: 100%;
|
||||
width: $s-16;
|
||||
width: var(--sp-l);
|
||||
}
|
||||
|
||||
.icon-dropdown svg {
|
||||
|
@ -37,42 +38,114 @@
|
|||
}
|
||||
|
||||
.cta-bottom-section {
|
||||
border-block-start: $s-1 solid var(--color-background-quaternary);
|
||||
display: grid;
|
||||
border-block-start: $b-1 solid var(--color-background-quaternary);
|
||||
color: var(--color-foreground-secondary);
|
||||
grid-template-columns: 1fr auto;
|
||||
margin-block-start: $s-12;
|
||||
padding-block-start: $s-12;
|
||||
margin-block-start: var(--sp-m);
|
||||
padding-block-start: var(--sp-m);
|
||||
}
|
||||
.cta-bottom-section .content {
|
||||
@include t.use-typography("body-small");
|
||||
@include buttonStyle;
|
||||
color: var(--color-foreground-secondary);
|
||||
display: inline;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cta-text,
|
||||
.cta-text-m,
|
||||
.cta-title {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
margin-block-end: var(--sp-xs);
|
||||
}
|
||||
|
||||
.cta-text {
|
||||
@include t.use-typography("body-small");
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
.cta-text-m {
|
||||
@include t.use-typography("body-medium");
|
||||
}
|
||||
|
||||
.cta-bottom-section .content strong,
|
||||
.cta-highlight {
|
||||
.cta-bottom-section .content a {
|
||||
@include t.use-typography("body-small");
|
||||
color: var(--color-accent-tertiary);
|
||||
margin-inline-start: var(--sp-xs);
|
||||
}
|
||||
|
||||
.cta-link {
|
||||
@include buttonStyle;
|
||||
align-self: end;
|
||||
margin-inline-start: $s-4;
|
||||
margin-inline-start: var(--sp-xs);
|
||||
}
|
||||
|
||||
.team {
|
||||
display: grid;
|
||||
grid-auto-rows: min-content;
|
||||
gap: var(--sp-s);
|
||||
max-width: $s-1000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.team-label {
|
||||
@include t.use-typography("headline-small");
|
||||
color: var(--title-foreground-color);
|
||||
}
|
||||
|
||||
.team-text {
|
||||
@include t.use-typography("body-large");
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.manage-subscription-link {
|
||||
@include buttonStyle;
|
||||
@include t.use-typography("body-small");
|
||||
color: var(--color-accent-tertiary);
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.subscription-icon {
|
||||
@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);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
@extend .menu-item-base;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--menu-foreground-color-hover);
|
||||
|
||||
.open-arrow {
|
||||
svg {
|
||||
stroke: var(--menu-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.members-cta {
|
||||
height: fit-content;
|
||||
margin-block-start: var(--sp-s);
|
||||
margin-inline-start: $s-68;
|
||||
max-width: $s-200;
|
||||
}
|
||||
|
||||
.members-cta-full-width {
|
||||
max-width: $s-1000;
|
||||
}
|
||||
|
||||
.cta-message {
|
||||
@include t.use-typography("body-small");
|
||||
color: var(--color-foreground-secondary);
|
||||
|
||||
a {
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.dashboard.change-owner]
|
||||
[app.main.ui.dashboard.subscription :refer [team*
|
||||
members-cta*
|
||||
show-subscription-members-main-banner?
|
||||
show-subscription-invitations-main-banner?]]
|
||||
[app.main.ui.dashboard.team-form]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -537,10 +541,22 @@
|
|||
|
||||
[:*
|
||||
[:& header {:section :dashboard-team-members :team team}]
|
||||
[:section {:class (stl/css :dashboard-container :dashboard-team-members)}
|
||||
[:section {:class (stl/css-case
|
||||
:dashboard-container true
|
||||
:dashboard-team-members true
|
||||
:dashboard-top-cta (show-subscription-members-main-banner? team profile))}
|
||||
(when (and (contains? cfg/flags :subscriptions)
|
||||
(show-subscription-members-main-banner? team profile))
|
||||
[:> members-cta* {:banner-is-expanded true :team team :profile profile}])
|
||||
[:> team-members*
|
||||
{:profile profile
|
||||
:team team}]]])
|
||||
: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}])]])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; INVITATIONS SECTION
|
||||
|
@ -803,8 +819,18 @@
|
|||
[:*
|
||||
[:& header {:section :dashboard-team-invitations
|
||||
:team team}]
|
||||
[:section {:class (stl/css :dashboard-team-invitations)}
|
||||
[:> invitation-section* {:team team}]]])
|
||||
[:section {:class (stl/css-case
|
||||
:dashboard-team-invitations true
|
||||
:dashboard-top-cta (show-subscription-invitations-main-banner? team))}
|
||||
(when (and (contains? cfg/flags :subscriptions)
|
||||
(show-subscription-invitations-main-banner? team))
|
||||
[:> members-cta* {:banner-is-expanded true :team team}])
|
||||
[:> 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}])]])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; WEBHOOKS SECTION
|
||||
|
@ -1159,5 +1185,8 @@
|
|||
[:div {:class (stl/css :block-content)}
|
||||
document-icon
|
||||
[:span {:class (stl/css :block-text)}
|
||||
(tr "labels.num-of-files" (i18n/c (:files stats)))]]]]]))
|
||||
(tr "labels.num-of-files" (i18n/c (:files stats)))]]]
|
||||
|
||||
(when (contains? cfg/flags :subscriptions)
|
||||
[:> team* {:is-owner (:is-owner permissions) :team team}])]]))
|
||||
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
|
||||
// Dashboard team settings
|
||||
.dashboard-team-settings {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: $s-24;
|
||||
width: 100%;
|
||||
border-top: $s-1 solid var(--panel-border-color);
|
||||
overflow-y: auto;
|
||||
padding-inline: $s-24;
|
||||
}
|
||||
|
||||
.block {
|
||||
|
@ -23,7 +24,7 @@
|
|||
grid-auto-rows: min-content;
|
||||
gap: $s-8;
|
||||
max-width: $s-1000;
|
||||
width: $s-1000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-block {
|
||||
|
@ -105,22 +106,28 @@
|
|||
|
||||
// TEAM MEMBERS PAGE
|
||||
.dashboard-team-members {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: $s-20;
|
||||
padding-inline-start: $s-20;
|
||||
padding-block-start: $s-20;
|
||||
border-top: $s-1 solid var(--panel-border-color);
|
||||
overflow-y: auto;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.dashboard-team-members.dashboard-top-cta {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.team-members {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
height: fit-content;
|
||||
max-width: $s-1000;
|
||||
width: $s-1000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
|
@ -275,22 +282,28 @@
|
|||
|
||||
// TEAM INVITATION PAGE
|
||||
.dashboard-team-invitations {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: $s-20;
|
||||
padding-inline-start: $s-20;
|
||||
padding-block-start: $s-20;
|
||||
border-top: $s-1 solid var(--panel-border-color);
|
||||
overflow-y: auto;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.dashboard-team-invitations .dashboard-top-cta {
|
||||
flex-direction: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.invitations {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
height: fit-content;
|
||||
max-width: $s-1000;
|
||||
width: $s-1000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-row-invitations {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
(def ^:svg-id loader "loader")
|
||||
(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 marketing-arrows "marketing-arrows")
|
||||
(def ^:svg-id marketing-exchange "marketing-exchange")
|
||||
(def ^:svg-id marketing-file "marketing-file")
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
(def ^:icon logo-icon (icon-xref :penpot-logo-icon))
|
||||
(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 brand-openid (icon-xref :brand-openid))
|
||||
(def ^:icon brand-github (icon-xref :brand-github))
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
[:& options-page]
|
||||
|
||||
:settings-subscription
|
||||
[:> subscription-page*]
|
||||
[:> subscription-page* {:profile profile}]
|
||||
|
||||
:settings-access-tokens
|
||||
[:& access-tokens-page]
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
(ns app.main.ui.settings.subscription
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.router :as rt]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc plan-card*
|
||||
{::mf/props :obj}
|
||||
[{:keys [card-title card-title-icon price-value price-period benefits-title benefits cta-text cta-link]}]
|
||||
[{: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]}]
|
||||
[:div {:class (stl/css :plan-card)}
|
||||
[:div {:class (stl/css :plan-card-header)}
|
||||
[:div {:class (stl/css :plan-card-title-container)}
|
||||
|
@ -23,25 +31,195 @@
|
|||
(when benefits-title [:h5 {:class (stl/css :benefits-title)} benefits-title])
|
||||
[:ul {:class (stl/css :benefits-list)}
|
||||
(for [benefit benefits]
|
||||
[:li {:key (str benefit) :class (stl/css :benefit)} "- " benefit])]
|
||||
(when (and cta-link cta-text) [:a {:class (stl/css :cta-button)
|
||||
:href cta-link} cta-text])])
|
||||
[:li {:key (dm/str benefit) :class (stl/css :benefit)} "- " benefit])]
|
||||
(when (and cta-link-with-icon cta-text-with-icon) [:button {:class (stl/css :cta-button :more-info)
|
||||
:on-click cta-link-with-icon} cta-text-with-icon i/open-link])
|
||||
(when (and cta-link cta-text) [:button {:class (stl/css-case :cta-button true
|
||||
:bottom-link (not (and cta-link-trial cta-text-trial)))
|
||||
:on-click cta-link} cta-text])
|
||||
(when (and cta-link-trial cta-text-trial) [:button {:class (stl/css :cta-button :bottom-link)
|
||||
:on-click cta-link-trial} cta-text-trial])])
|
||||
|
||||
(mf/defc subscribe-management-dialog
|
||||
{::mf/register modal/components
|
||||
::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))
|
||||
min-members (deref min-members*)
|
||||
formatted-subscription-name (if subscribe-to-trial
|
||||
(if (= subscription-name "unlimited")
|
||||
(tr "subscription.settings.unlimited-trial")
|
||||
(tr "subscription.settings.enterprise-trial"))
|
||||
(case subscription-name
|
||||
"professional" (tr "subscription.settings.professional")
|
||||
"unlimited" (tr "subscription.settings.unlimited")
|
||||
"enterprise" (tr "subscription.settings.enterprise")))
|
||||
handle-subscription-trial (if "unlimited"
|
||||
(mf/use-fn
|
||||
(mf/deps min-members)
|
||||
(fn []
|
||||
;; TODO add event tracking subscribe trial unlimited
|
||||
(let [current-href (rt/get-current-href)
|
||||
returnUrl (js/encodeURIComponent current-href)
|
||||
href (dm/str "payments/subscriptions/create?type=unlimited&quantity=" min-members "&returnUrl=" returnUrl)]
|
||||
(st/emit! (rt/nav-raw :href href)))))
|
||||
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
;; TODO add event tracking subscribe trial 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-callback
|
||||
(fn []
|
||||
;; TODO add event subscribe to another subscription
|
||||
(let [current-href (rt/get-current-href)
|
||||
returnUrl (js/encodeURIComponent current-href)
|
||||
href (dm/str "payments/subscriptions/show?returnUrl=" returnUrl)]
|
||||
(st/emit! (rt/nav-raw :href href)))
|
||||
(modal/hide!)))
|
||||
handle-close-dialog (mf/use-callback
|
||||
(fn []
|
||||
;; TODO add event tracking close modal/cancel subscription
|
||||
(modal/hide!)))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog)}
|
||||
[:button {:class (stl/css :close-btn) :on-click handle-close-dialog} i/close]
|
||||
[:div {:class (stl/css :modal-title :subscription-title)}
|
||||
(tr "subscription.settings.management.dialog.title" formatted-subscription-name)]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
(if (seq teams)
|
||||
[* [:div {:class (stl/css :modal-text)}
|
||||
(tr "subscription.settings.management.dialog.choose-this-plan")]
|
||||
[: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))])]]
|
||||
[:div {:class (stl/css :modal-text)}
|
||||
(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
|
||||
: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))))}]]
|
||||
[: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
|
||||
(or (= subscription-name "professional") (= subscription-name "unlimited"))
|
||||
(not subscribe-to-trial))
|
||||
[:div {:class (stl/css :modal-text)}
|
||||
(tr "subscription.settings.management.dialog.downgrade")])
|
||||
|
||||
[: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 handle-subscription-trial handle-accept-dialog)}]]]]]]))
|
||||
|
||||
(mf/defc subscription-success-dialog
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :subscription-success}
|
||||
[{:keys [subscription-name]}]
|
||||
|
||||
(let [handle-close-dialog (mf/use-callback
|
||||
(fn []
|
||||
;; TODO add event tracking close modal
|
||||
(modal/hide!)))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog :subscription-success)}
|
||||
[: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]
|
||||
|
||||
[:div {:class (stl/css :modal-end)}
|
||||
[:div {:class (stl/css :modal-title)} (tr "subscription.settings.sucess.dialog.title" subscription-name)]
|
||||
[:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.success.dialog.description")]
|
||||
[:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.sucess.dialog.footer")]
|
||||
|
||||
[:div {:class (stl/css :success-action-buttons)}
|
||||
[:input
|
||||
{:class (stl/css :primary-button)
|
||||
:type "button"
|
||||
:value (tr "labels.close")
|
||||
:on-click handle-close-dialog}]]]]]]))
|
||||
|
||||
(mf/defc subscription-page*
|
||||
[]
|
||||
(let [;; TODO subscription cases professional/unlimited/enterprise
|
||||
subscription-name :unlimited
|
||||
subscription-is-trial false
|
||||
[{:keys [profile]}]
|
||||
(let [route (mf/deref refs/route)
|
||||
params (:params route)
|
||||
show-subscription-success-modal (and (:query params)
|
||||
(or (= (:subscription (:query params)) "subscribed-to-penpot-unlimited")
|
||||
(= (:subscription (:query params)) "subscribed-to-penpot-enterprise")))
|
||||
subscription (:subscription (:props profile))
|
||||
subscription-name (if subscription
|
||||
(:type subscription)
|
||||
"professional")
|
||||
subscription-is-trial (= (:status subscription) "trialing")
|
||||
teams* (mf/use-state nil)
|
||||
teams (deref teams*)
|
||||
locale (mf/deref i18n/locale)
|
||||
profile (mf/deref refs/profile)
|
||||
penpot-member (dt/format-date-locale-short (:created-at profile) {:locale locale})
|
||||
;; TODO get subscription member date
|
||||
subscription-member "January 17, 2024"
|
||||
;; TODO update url to penpot payments
|
||||
go-to-payments "https://penpot.app/pricing"]
|
||||
subscription-member (dt/format-date-locale-short (:start-date subscription) {:locale locale})
|
||||
go-to-pricing-page (mf/use-fn
|
||||
(fn []
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "explore-pricing-click" ::ev/origin "settings" :section "subscription"}))
|
||||
(dom/open-new-window "https://penpot.app/pricing")))
|
||||
go-to-payments (mf/use-fn
|
||||
(fn []
|
||||
;; TODO add event tracking manage subscription in stripe
|
||||
(let [current-href (rt/get-current-href)
|
||||
returnUrl (js/encodeURIComponent current-href)
|
||||
href (dm/str "payments/subscriptions/show?returnUrl=" returnUrl)]
|
||||
(st/emit! (rt/nav-raw :href href)))))
|
||||
open-subscription-modal (mf/use-fn
|
||||
(mf/deps teams)
|
||||
(fn [subscription-name]
|
||||
;; TODO add event tracking open modal to try trial
|
||||
(st/emit!
|
||||
(modal/show :management-dialog
|
||||
{:subscription-name subscription-name
|
||||
:teams teams :subscribe-to-trial (not subscription)}))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(->> (rp/cmd! :get-owned-teams)
|
||||
(rx/subs! (fn [teams]
|
||||
(reset! teams* teams)))))
|
||||
|
||||
(mf/with-effect []
|
||||
(dom/set-html-title (tr "subscription.labels")))
|
||||
|
||||
(when show-subscription-success-modal
|
||||
;; add name subscription from params
|
||||
(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"))})))
|
||||
[:section {:class (stl/css :dashboard-section)}
|
||||
[:div {:class (stl/css :dashboard-content)}
|
||||
[:h2 {:class (stl/css :title-section)} (tr "subscription.labels")]
|
||||
|
@ -50,13 +228,13 @@
|
|||
[:div {:class (stl/css :your-subscription)}
|
||||
[:h3 {:class (stl/css :plan-section-title)} (tr "subscription.settings.section-plan")]
|
||||
(case subscription-name
|
||||
:professional
|
||||
"professional"
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.professional")
|
||||
:benefits [(tr "subscription.settings.professional.projects-files"),
|
||||
(tr "subscription.settings.professional.teams-editors"),
|
||||
(tr "subscription.settings.professional.storage")]}]
|
||||
|
||||
:unlimited
|
||||
"unlimited"
|
||||
(if subscription-is-trial
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.unlimited-trial")
|
||||
:card-title-icon i/character-u
|
||||
|
@ -65,7 +243,9 @@
|
|||
(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
|
||||
:cta-text-trial (tr "subscription.settings.add-payment-to-continue")
|
||||
:cta-link-trial go-to-payments}]
|
||||
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.unlimited")
|
||||
:card-title-icon i/character-u
|
||||
|
@ -76,10 +256,10 @@
|
|||
:cta-text (tr "subscription.settings.manage-your-subscription")
|
||||
:cta-link go-to-payments}])
|
||||
|
||||
:enterprise
|
||||
"enterprise"
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.enterprise")
|
||||
:card-title-icon i/character-e
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professiona-benefits")
|
||||
: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")]
|
||||
|
@ -97,36 +277,42 @@
|
|||
|
||||
[:div {:class (stl/css :other-subscriptions)}
|
||||
[:h3 {:class (stl/css :plan-section-title)} (tr "subscription.settings.other-plans")]
|
||||
(when (not= subscription-name :professional)
|
||||
(when (not= subscription-name "professional")
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.professional")
|
||||
:price-value "$0"
|
||||
:price-period (tr "subscription.settings.price-editor-month")
|
||||
:benefits [(tr "subscription.settings.professional.projects-files"),
|
||||
(tr "subscription.settings.professional.teams-editors"),
|
||||
(tr "subscription.settings.professional.storage")]
|
||||
:cta-text (tr "subscription.dashboard.power-up.subscribe")
|
||||
:cta-link go-to-payments}])
|
||||
:cta-text (tr "subscription.settings.subscribe")
|
||||
:cta-link #(open-subscription-modal "professional")
|
||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||
:cta-link-with-icon go-to-pricing-page}])
|
||||
|
||||
(when (not= subscription-name :unlimited)
|
||||
(when (not= subscription-name "unlimited")
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.unlimited")
|
||||
:card-title-icon i/character-u
|
||||
:price-value "$7"
|
||||
:price-period (tr "subscription.settings.price-editor-month")
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professiona-benefits")
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professional-benefits")
|
||||
:benefits [(tr "subscription.settings.unlimited.teams"),
|
||||
(tr "subscription.settings.unlimited.bill"),
|
||||
(tr "subscription.settings.unlimited.storage")]
|
||||
:cta-text (tr "subscription.settings.ulimited.try-it-free")
|
||||
:cta-link go-to-payments}])
|
||||
:cta-text (if subscription (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
|
||||
:cta-link #(open-subscription-modal "unlimited")
|
||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||
:cta-link-with-icon go-to-pricing-page}])
|
||||
|
||||
(when (not= subscription-name :enterprise)
|
||||
(when (not= subscription-name "enterprise")
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.enterprise")
|
||||
:card-title-icon i/character-e
|
||||
:price-value "$950"
|
||||
:price-period (tr "subscription.settings.price-organization-month")
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professiona-benefits")
|
||||
: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.dashboard.power-up.subscribe")
|
||||
:cta-link go-to-payments}])]]]))
|
||||
:cta-text (if subscription (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
|
||||
:cta-link #(open-subscription-modal "enterprise")
|
||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||
:cta-link-with-icon go-to-pricing-page}])]]]))
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
@use "common/refactor/common-refactor.scss" as *;
|
||||
@use "../ds/typography.scss" as t;
|
||||
@use "../ds/_borders.scss" as *;
|
||||
@use "../ds/spacing.scss" as *;
|
||||
|
||||
.dashboard-section {
|
||||
display: flex;
|
||||
|
@ -19,30 +21,30 @@
|
|||
justify-content: center;
|
||||
flex-direction: column;
|
||||
max-width: $s-500;
|
||||
margin-bottom: $s-32;
|
||||
margin-block-end: var(--sp-xxxl);
|
||||
width: $s-580;
|
||||
margin: $s-92 auto $s-120 auto;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.membership-container {
|
||||
margin-block-start: $s-16;
|
||||
margin-block-start: var(--sp-l);
|
||||
}
|
||||
|
||||
.membership {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-block-start: $s-8;
|
||||
margin-block-start: var(--sp-s);
|
||||
}
|
||||
|
||||
.membership.first {
|
||||
margin-block-start: $s-16;
|
||||
margin-block-start: var(--sp-l);
|
||||
}
|
||||
|
||||
.membership-date {
|
||||
@include t.use-typography("body-small");
|
||||
color: var(--color-foreground-secondary);
|
||||
margin-inline-start: $s-8;
|
||||
margin-inline-start: var(--sp-s);
|
||||
}
|
||||
|
||||
.subscription-member,
|
||||
|
@ -61,7 +63,7 @@
|
|||
.title-section {
|
||||
@include t.use-typography("title-large");
|
||||
color: var(--color-foreground-primary);
|
||||
margin-block-end: $s-16;
|
||||
margin-block-end: var(--sp-l);
|
||||
}
|
||||
|
||||
.plan-section-title {
|
||||
|
@ -70,29 +72,32 @@
|
|||
}
|
||||
|
||||
.plan-card {
|
||||
border: $s-1 solid var(--color-foreground-secondary);
|
||||
border-radius: $s-8;
|
||||
margin-block-start: $s-16;
|
||||
padding: $s-16;
|
||||
border: $b-1 solid var(--color-foreground-secondary);
|
||||
border-radius: var(--sp-s);
|
||||
margin-block-start: var(--sp-l);
|
||||
padding: var(--sp-l);
|
||||
}
|
||||
|
||||
.plan-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-block-end: $s-8;
|
||||
margin-block-end: var(--sp-s);
|
||||
}
|
||||
|
||||
.plan-card-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $s-8;
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.plan-title-icon {
|
||||
@extend .button-icon;
|
||||
stroke: var(--color-foreground-primary);
|
||||
border-radius: $s-4;
|
||||
border: $s-1 solid var(--color-foreground-primary);
|
||||
height: var(--sp-xl);
|
||||
width: var(--sp-xl);
|
||||
border-radius: var(--sp-xs);
|
||||
border: $b-1 solid var(--color-foreground-primary);
|
||||
padding: $s-1;
|
||||
}
|
||||
|
||||
.plan-card-title,
|
||||
|
@ -122,5 +127,134 @@
|
|||
|
||||
.cta-button {
|
||||
@include t.use-typography("body-small");
|
||||
@include buttonStyle;
|
||||
color: var(--color-accent-tertiary);
|
||||
display: flex;
|
||||
margin-block-start: var(--sp-m);
|
||||
}
|
||||
|
||||
.cta-button svg {
|
||||
@extend .button-icon;
|
||||
height: var(--sp-l);
|
||||
width: var(--sp-l);
|
||||
stroke: var(--color-accent-tertiary);
|
||||
margin-inline-start: var(--sp-xs);
|
||||
}
|
||||
|
||||
.bottom-link {
|
||||
margin-block-start: var(--sp-xs);
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
@extend .modal-overlay-base;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
@extend .modal-container-base;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
max-height: initial;
|
||||
min-width: $s-520;
|
||||
}
|
||||
|
||||
.modal-dialog.subscription-success {
|
||||
min-width: $s-612;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@extend .modal-close-btn-base;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include t.use-typography("title-large");
|
||||
margin-block-end: var(--sp-xxxl);
|
||||
color: var(--modal-title-foreground-color);
|
||||
display: flex;
|
||||
gap: var(--sp-m);
|
||||
}
|
||||
|
||||
.subscription-title {
|
||||
margin-block-end: var(--sp-l);
|
||||
}
|
||||
|
||||
.modal-text-lage {
|
||||
@include t.use-typography("body-large");
|
||||
}
|
||||
|
||||
.modal-text-small {
|
||||
@include t.use-typography("body-small");
|
||||
}
|
||||
|
||||
.modal-content,
|
||||
.modal-end {
|
||||
color: var(--color-foreground-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-success-content {
|
||||
display: flex;
|
||||
gap: $s-40;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
margin-block-start: $s-40;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@extend .modal-action-btns;
|
||||
}
|
||||
|
||||
.success-action-buttons {
|
||||
margin-block-start: var(--sp-l);
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
@extend .modal-accept-btn;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
@extend .modal-cancel-btn;
|
||||
}
|
||||
|
||||
.modal-start {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-width: $s-220;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.teams-list {
|
||||
list-style-position: inside;
|
||||
list-style-type: disc;
|
||||
margin-inline-start: var(--sp-xl);
|
||||
margin-block: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
@extend .input-element;
|
||||
width: $s-80;
|
||||
}
|
||||
|
||||
.editors-label {
|
||||
margin-block-start: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.editors-wrapper {
|
||||
display: flex;
|
||||
gap: var(--sp-xl);
|
||||
margin-block-start: var(--sp-l);
|
||||
}
|
||||
|
||||
.editors-cost {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.dashboard.subscription :refer [main-menu-power-up*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.hooks.resize :as r]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -819,7 +820,12 @@
|
|||
(reset! sub-menu* nil)
|
||||
(st/emit!
|
||||
(ptk/event ::ev/event {::ev/name "open-plugins-manager" ::ev/origin "workspace:menu"})
|
||||
(modal/show :plugin-management {}))))]
|
||||
(modal/show :plugin-management {}))))
|
||||
|
||||
subscription (:subscription (:props profile))
|
||||
subscription-name (if subscription
|
||||
(:type subscription)
|
||||
"professional")]
|
||||
|
||||
(mf/with-effect []
|
||||
(let [disposable (->> st/stream
|
||||
|
@ -904,6 +910,10 @@
|
|||
:id "file-menu-help-info"}
|
||||
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.help-info")]
|
||||
[:span {:class (stl/css :open-arrow)} i/arrow]]
|
||||
|
||||
(when (and (contains? cf/flags :subscriptions) (not= "enterprise" subscription-name))
|
||||
[:> main-menu-power-up* {:close-sub-menu close-sub-menu}])
|
||||
|
||||
;; TODO remove this block when subscriptions is full implemented
|
||||
(when (contains? cf/flags :subscriptions-old)
|
||||
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
|
||||
|
@ -913,7 +923,7 @@
|
|||
(on-power-up-click)))
|
||||
:on-pointer-enter close-sub-menu
|
||||
:id "file-menu-power-up"}
|
||||
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.power-up")]])]
|
||||
[:span {:class (stl/css :item-name)} (tr "subscription.workspace.header.menu.option.power-up")]])]
|
||||
|
||||
(case sub-menu
|
||||
:file
|
||||
|
|
|
@ -1067,11 +1067,72 @@ msgstr "No limits on creativity"
|
|||
msgid "dashboard.upgrade-plan.penpot-free"
|
||||
msgstr "Penpot Free"
|
||||
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs:977, src/app/main/ui/dashboard/subscription.cljs:54
|
||||
msgid "dashboard.upgrade-plan.power-up"
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs:972
|
||||
msgid "subscription.dashboard.upgrade-plan.power-up"
|
||||
msgstr "Power up"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:924
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:910
|
||||
msgid "subscription.workspace.header.menu.option.power-up"
|
||||
msgstr "Power up your plan"
|
||||
|
||||
msgid "subscription.dashboard.team-plan"
|
||||
msgstr "Team plan"
|
||||
|
||||
msgid "subscription.dashboard.power-up.your-subscription"
|
||||
msgstr "Your subscription:"
|
||||
|
||||
msgid "subscription.dashboard.power-up.professional.top-title"
|
||||
msgstr "Professional plan"
|
||||
|
||||
msgid "subscription.dashboard.power-up.unlimited-plan"
|
||||
msgstr "Unlimited plan"
|
||||
|
||||
msgid "subscription.dashboard.power-up.enterprise-plan"
|
||||
msgstr "Enterprise plan"
|
||||
|
||||
msgid "subscription.dashboard.power-up.trial.top-title"
|
||||
msgstr "Unlimited plan (trial)"
|
||||
|
||||
msgid "subscription.dashboard.cta.professional-plan-designed"
|
||||
msgstr "The Professional plan is designed for teams of up to 8 editors (owner, admin, and editor)."
|
||||
|
||||
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."
|
||||
|
||||
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)."
|
||||
|
||||
#, markdown
|
||||
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)"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner-more-seats"
|
||||
msgstr ""
|
||||
"Please upgrade to match your usage. "
|
||||
"[Subscribe now.](%s)"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-member"
|
||||
msgstr ""
|
||||
"Get more editors, more storage, and more autosaved versions with the Unlimited or Enterprise plan. Contact with the team owner: "
|
||||
"%s"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.cta.upgrade-to-full-access-owner"
|
||||
msgstr ""
|
||||
"Unlock full access forever. "
|
||||
"[Subscribe now.](%s)"
|
||||
|
||||
#, 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/team.cljs:909
|
||||
msgid "dashboard.webhooks.active"
|
||||
msgstr "Is active"
|
||||
|
||||
|
@ -4267,13 +4328,6 @@ msgstr "Enterprise plan"
|
|||
msgid "subscription.dashboard.power-up.enterprise.description"
|
||||
msgstr "Advanced security, activity logs, dedicated support and more."
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:53
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.power-up.professional.bottom-description"
|
||||
msgstr ""
|
||||
"Get extra editors and storage, file backup and more with the **Unlimited "
|
||||
"plan**"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:51
|
||||
msgid "subscription.dashboard.power-up.professional.top-title"
|
||||
msgstr "Professional plan"
|
||||
|
@ -4282,10 +4336,6 @@ msgstr "Professional plan"
|
|||
msgid "subscription.dashboard.power-up.subscribe"
|
||||
msgstr "Subscribe"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:63
|
||||
msgid "subscription.dashboard.power-up.trial.bottom-description"
|
||||
msgstr "Enjoying your trial? Unlock full access forever."
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:62
|
||||
msgid "subscription.dashboard.power-up.trial.top-description"
|
||||
msgstr "Extra editors, storage and autosaved version, file backup and more."
|
||||
|
@ -4298,12 +4348,25 @@ msgstr "Unlimited plan (trial)"
|
|||
msgid "subscription.dashboard.power-up.unlimited-plan"
|
||||
msgstr "Unlimited plan"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:53
|
||||
#, markdown
|
||||
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)"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:69
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.power-up.unlimited.bottom-description"
|
||||
msgstr ""
|
||||
"Get advanced security, activity logs, dedicated support and more with "
|
||||
"**Enterprise plan**"
|
||||
"Get advanced security, activity logs, dedicated support and more. Take a look to the"
|
||||
"[Enterprise plan.](%s)"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.power-up.trial.bottom-description"
|
||||
msgstr ""
|
||||
"Enjoying your trial? Unlock full access forever."
|
||||
"[Subscribe](%s)"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:70
|
||||
msgid "subscription.dashboard.power-up.unlimited.cta"
|
||||
|
@ -4466,7 +4529,127 @@ msgstr "Password - Penpot"
|
|||
msgid "title.settings.profile"
|
||||
msgstr "Profile - Penpot"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:795
|
||||
msgid "subscription.settings.section-plan"
|
||||
msgstr "Your subscription"
|
||||
|
||||
msgid "subscription.settings.other-plans"
|
||||
msgstr "Other penpot plans"
|
||||
|
||||
msgid "subscription.settings.unlimited"
|
||||
msgstr "Unlimited"
|
||||
|
||||
msgid "subscription.settings.unlimited-trial"
|
||||
msgstr "Unlimited (trial)"
|
||||
|
||||
msgid "subscription.settings.unlimited-trial-modal"
|
||||
msgstr "Unlimited trial"
|
||||
|
||||
msgid "subscription.settings.enterprise"
|
||||
msgstr "Enterprise"
|
||||
|
||||
msgid "subscription.settings.enterprise-trial"
|
||||
msgstr "Enterprise (trial)"
|
||||
|
||||
msgid "subscription.settings.enterprise-trial-modal"
|
||||
msgstr "Enterprise trial"
|
||||
|
||||
msgid "subscription.settings.support-us-since"
|
||||
msgstr "You've been supporting us with this plan since %s"
|
||||
|
||||
msgid "subscription.settings.member-since"
|
||||
msgstr "Penpot member since %s"
|
||||
|
||||
msgid "subscription.settings.price-editor-month"
|
||||
msgstr "editor per month"
|
||||
|
||||
msgid "subscription.settings.price-organization-month"
|
||||
msgstr "organization per month"
|
||||
|
||||
msgid "subscription.settings.more-information"
|
||||
msgstr "More information"
|
||||
|
||||
msgid "subscription.settings.try-it-free"
|
||||
msgstr "Try it free for 14 days"
|
||||
|
||||
msgid "subscription.settings.subscribe"
|
||||
msgstr "Subscribe"
|
||||
|
||||
msgid "subscription.settings.start-trial"
|
||||
msgstr "Start free trial"
|
||||
|
||||
msgid "subscription.settings.professional.projects-files"
|
||||
msgstr "Unlimited projects, files and drafts"
|
||||
|
||||
msgid "subscription.settings.professional.teams-editors"
|
||||
msgstr "Unlimited teams of up to 8 editors"
|
||||
|
||||
msgid "subscription.settings.professional.storage"
|
||||
msgstr "10GB of storage and 7-day autosave versions"
|
||||
|
||||
msgid "subscription.settings.benefits.all-professional-benefits"
|
||||
msgstr "All Professional plan benefits and:"
|
||||
|
||||
msgid "subscription.settings.benefits.all-unlimited-benefits"
|
||||
msgstr "All Unlimited plan benefits and:"
|
||||
|
||||
msgid "subscription.settings.unlimited.teams"
|
||||
msgstr "Unlimited teams, no matter your team size"
|
||||
|
||||
msgid "subscription.settings.unlimited.bill"
|
||||
msgstr "Capped monthly bill"
|
||||
|
||||
msgid "subscription.settings.unlimited.storage"
|
||||
msgstr "25GB of storage and 30-day autosave versions and file backup"
|
||||
|
||||
msgid "subscription.settings.manage-your-subscription"
|
||||
msgstr "Manage your subscription"
|
||||
|
||||
msgid "subscription.settings.add-payment-to-continue"
|
||||
msgstr "Add a payment method to continue after your trial"
|
||||
|
||||
msgid "subscription.settings.enterprise.support"
|
||||
msgstr "Dedicated support"
|
||||
|
||||
msgid "subscription.settings.enterprise.security"
|
||||
msgstr "Advanced security"
|
||||
|
||||
msgid "subscription.settings.enterprise.logs"
|
||||
msgstr "Activity logs"
|
||||
|
||||
msgid "subscription.settings.sucess.dialog.title"
|
||||
msgstr "You are %s!"
|
||||
|
||||
msgid "subscription.settings.success.dialog.description"
|
||||
msgstr "You can edit your subscription at any time from the 'Subscription' page in your account details."
|
||||
|
||||
msgid "subscription.settings.sucess.dialog.footer"
|
||||
msgstr "Enjoy your plan!"
|
||||
|
||||
msgid "subscription.settings.management.dialog.title"
|
||||
msgstr "Apply %s to your teams"
|
||||
|
||||
msgid "subscription.settings.management.dialog.choose-this-plan"
|
||||
msgstr "You are choosing this plan for:"
|
||||
|
||||
msgid "subscription.settings.management.dialog.members"
|
||||
msgstr " (%s editors)"
|
||||
|
||||
msgid "subscription.settings.management.dialog.no-teams"
|
||||
msgstr "This plan will apply to all future teams you create or own."
|
||||
|
||||
msgid "subscription.settings.management.dialog.select-editors"
|
||||
msgstr "Select number of editors (seats) you need:"
|
||||
|
||||
msgid "subscription.settings.management.dialog.price-month"
|
||||
msgstr "$7 per editor/month x %s"
|
||||
|
||||
msgid "subscription.settings.management.dialog.payment-explanation"
|
||||
msgstr "(No payment will be made now)"
|
||||
|
||||
msgid "subscription.settings.management.dialog.downgrade"
|
||||
msgstr "Heads up: switching to a lower plan means less storage and shorter backups and version history."
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:779
|
||||
msgid "title.team-invitations"
|
||||
msgstr "Invitations - %s - Penpot"
|
||||
|
||||
|
@ -4474,7 +4657,6 @@ msgstr "Invitations - %s - Penpot"
|
|||
msgid "title.team-members"
|
||||
msgstr "Members - %s - Penpot"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:1105
|
||||
msgid "title.team-settings"
|
||||
msgstr "Settings - %s - Penpot"
|
||||
|
||||
|
@ -4932,12 +5114,6 @@ msgstr "Help & info"
|
|||
#: src/app/main/ui/workspace/main_menu.cljs:916
|
||||
msgid "workspace.header.menu.option.power-up"
|
||||
msgstr "Power up your plan"
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:881
|
||||
msgid "workspace.header.menu.option.preferences"
|
||||
msgstr "Preferences"
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:870
|
||||
msgid "workspace.header.menu.option.view"
|
||||
msgstr "View"
|
||||
|
||||
|
|
|
@ -1079,11 +1079,86 @@ msgstr "Sin límites a la creatividad"
|
|||
msgid "dashboard.upgrade-plan.penpot-free"
|
||||
msgstr "Penpot Gratis"
|
||||
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs:977, src/app/main/ui/dashboard/subscription.cljs:54
|
||||
msgid "dashboard.upgrade-plan.power-up"
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs:972
|
||||
msgid "subscription.dashboard.upgrade-plan.power-up"
|
||||
msgstr "Mejora"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:924
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:910
|
||||
msgid "subscription.workspace.header.menu.option.power-up"
|
||||
msgstr "Mejora tu plan"
|
||||
|
||||
msgid "subscription.dashboard.team-plan"
|
||||
msgstr "Plan de equipo"
|
||||
|
||||
msgid "subscription.dashboard.power-up.professional.top-title"
|
||||
msgstr "Plan Professional"
|
||||
|
||||
#, markdown
|
||||
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)"
|
||||
|
||||
msgid "subscription.dashboard.power-up.unlimited-plan"
|
||||
msgstr "Plan ilimitado"
|
||||
|
||||
#, 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)"
|
||||
|
||||
msgid "subscription.dashboard.power-up.enterprise-plan"
|
||||
msgstr "Plan Enterprise"
|
||||
|
||||
msgid "subscription.dashboard.power-up.trial.top-title"
|
||||
msgstr "Plan Unlimited (Prueba)"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.power-up.trial.bottom-description"
|
||||
msgstr ""
|
||||
"¿Disfrutas de la prueba? Desbloquea el acceso completo para siempre."
|
||||
"[Suscríbete](%s)"
|
||||
|
||||
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."
|
||||
|
||||
msgid "subscription.dashboard.cta.professional-plan-designed"
|
||||
msgstr "El plan Professional está diseñado para equipos de hasta 8 editores (propietario, administrador y editor)."
|
||||
|
||||
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)."
|
||||
|
||||
#, markdown
|
||||
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)"
|
||||
|
||||
#, 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)"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-member"
|
||||
msgstr ""
|
||||
"Consigue más editores, más almacenamiento y más versiones guardadas automáticamente con el plan Unlimited o Enterprise. Contacta con el propietario del equipo: "
|
||||
"%s"
|
||||
|
||||
#, markdown
|
||||
msgid "subscription.dashboard.cta.upgrade-to-full-access-owner"
|
||||
msgstr ""
|
||||
"Desbloquea el acceso completo para siempre. "
|
||||
"[Suscríbete ahora.](%s)"
|
||||
|
||||
#, 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/team.cljs:909
|
||||
msgid "dashboard.webhooks.active"
|
||||
msgstr "Activo"
|
||||
|
||||
|
@ -4489,7 +4564,121 @@ msgstr "Contraseña - Penpot"
|
|||
msgid "title.settings.profile"
|
||||
msgstr "Perfil - Penpot"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:795
|
||||
msgid "subscription.settings.section-plan"
|
||||
msgstr "Tu suscripción"
|
||||
|
||||
msgid "subscription.settings.other-plans"
|
||||
msgstr "Otros planes de penpot"
|
||||
|
||||
msgid "subscription.settings.professional"
|
||||
msgstr "Professional"
|
||||
|
||||
msgid "subscription.settings.unlimited"
|
||||
msgstr "Unlimited"
|
||||
|
||||
msgid "subscription.settings.unlimited-trial"
|
||||
msgstr "Unlimited (prueba)"
|
||||
|
||||
msgid "subscription.settings.unlimited-trial-modal"
|
||||
msgstr "Unlimited de prueba"
|
||||
|
||||
msgid "subscription.settings.enterprise"
|
||||
msgstr "Enterprise"
|
||||
|
||||
msgid "subscription.settings.enterprise-trial"
|
||||
msgstr "Enterprise (prueba)"
|
||||
|
||||
msgid "subscription.settings.enterprise-trial-modal"
|
||||
msgstr "Enterprise de prueba"
|
||||
|
||||
msgid "subscription.settings.support-us-since"
|
||||
msgstr "Nos has estado apoyando con este plan desde %s"
|
||||
|
||||
msgid "subscription.settings.member-since"
|
||||
msgstr "Miembro de penpot desde %s"
|
||||
|
||||
msgid "subscription.settings.price-editor-month"
|
||||
msgstr "editor por mes"
|
||||
|
||||
msgid "subscription.settings.price-organization-month"
|
||||
msgstr "organización por mes"
|
||||
|
||||
msgid "subscription.settings.try-it-free"
|
||||
msgstr "Pruébalo gratis durante 14 días"
|
||||
|
||||
msgid "subscription.settings.professional.projects-files"
|
||||
msgstr "Proyectos, archivos y borradores ilimitados"
|
||||
|
||||
msgid "subscription.settings.professional.teams-editors"
|
||||
msgstr "Equipos ilimitados de hasta 8 redactores"
|
||||
|
||||
msgid "subscription.settings.professional.storage"
|
||||
msgstr "10 GB de almacenamiento y versiones de autoguardado de 7 días"
|
||||
|
||||
msgid "subscription.settings.benefits.all-professional-benefits"
|
||||
msgstr "Todas las prestaciones del plan Professional y:"
|
||||
|
||||
msgid "subscription.settings.benefits.all-unlimited-benefits"
|
||||
msgstr "Todas las prestaciones del plan Unlimited y:"
|
||||
|
||||
msgid "subscription.settings.unlimited.teams"
|
||||
msgstr "Equipos ilimitados, independientemente de su tamaño"
|
||||
|
||||
msgid "subscription.settings.unlimited.bill"
|
||||
msgstr "Factura mensual limitada"
|
||||
|
||||
msgid "subscription.settings.unlimited.storage"
|
||||
msgstr "25 GB de almacenamiento y 30 días de autoguardado de versiones y copias de seguridad de archivos"
|
||||
|
||||
msgid "subscription.settings.manage-your-subscription"
|
||||
msgstr "Gestionar tu suscripción"
|
||||
|
||||
msgid "subscription.settings.add-payment-to-continue"
|
||||
msgstr "Añade un método de pago para continuar después del periodo de prueba"
|
||||
|
||||
msgid "subscription.settings.enterprise.support"
|
||||
msgstr "Apoyo específico"
|
||||
|
||||
msgid "subscription.settings.enterprise.security"
|
||||
msgstr "Seguridad avanzada"
|
||||
|
||||
msgid "subscription.settings.enterprise.logs"
|
||||
msgstr "Registros de actividad"
|
||||
|
||||
msgid "subscription.settings.sucess.dialog.title"
|
||||
msgstr "Eres %s!"
|
||||
|
||||
msgid "subscription.settings.success.dialog.description"
|
||||
msgstr "Puedes modificar tu suscripción en cualquier momento desde la página 'Suscripción' en los datos de tu cuenta."
|
||||
|
||||
msgid "subscription.settings.sucess.dialog.footer"
|
||||
msgstr "¡Disfruta de tu plan!"
|
||||
|
||||
msgid "subscription.settings.management.dialog.title"
|
||||
msgstr "Aplica %s a tus equipos"
|
||||
|
||||
msgid "subscription.settings.management.dialog.choose-this-plan"
|
||||
msgstr "Estás eligiendo este plan para:"
|
||||
|
||||
msgid "subscription.settings.management.dialog.members"
|
||||
msgstr " (%s editores)"
|
||||
|
||||
msgid "subscription.settings.management.dialog.no-teams"
|
||||
msgstr "Este plan se aplicará a todos los futuros equipos que crees o poseas."
|
||||
|
||||
msgid "subscription.settings.management.dialog.select-editors"
|
||||
msgstr "Seleccione el número de editores (puestos) que necesitas:"
|
||||
|
||||
msgid "subscription.settings.management.dialog.price-month"
|
||||
msgstr "$7 por editor/mes x %s"
|
||||
|
||||
msgid "subscription.settings.management.dialog.payment-explanation"
|
||||
msgstr "(Ahora no se efectuará ningún pago)"
|
||||
|
||||
msgid "subscription.settings.management.dialog.downgrade"
|
||||
msgstr "Ten en cuenta: cambiar a un plan inferior significa menos almacenamiento y copias de seguridad e historial de versiones más cortos."
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:779
|
||||
msgid "title.team-invitations"
|
||||
msgstr "Invitaciones - %s - Penpot"
|
||||
|
||||
|
@ -4954,10 +5143,6 @@ msgstr "Archivo"
|
|||
msgid "workspace.header.menu.option.help-info"
|
||||
msgstr "Ayuda e información"
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:916
|
||||
msgid "workspace.header.menu.option.power-up"
|
||||
msgstr "Mejora tu plan"
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:881
|
||||
msgid "workspace.header.menu.option.preferences"
|
||||
msgstr "Preferencias"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue