mirror of
https://github.com/penpot/penpot.git
synced 2026-03-20 17:33:44 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
@@ -82,6 +82,16 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(->> (rp/cmd! :get-profile)
|
||||
(rx/mapcat (fn [profile]
|
||||
(if (and (contains? cf/flags :subscriptions)
|
||||
(is-authenticated? profile))
|
||||
(->> (rp/cmd! :get-subscription-usage {})
|
||||
(rx/map (fn [{:keys [editors]}]
|
||||
(update-in profile [:props :subscription] assoc :editors editors)))
|
||||
(rx/catch (fn [cause]
|
||||
(js/console.error "unexpected error on obtaining subscription usage" cause)
|
||||
(rx/of profile))))
|
||||
(rx/of profile))))
|
||||
(rx/map (partial ptk/data-event ::profile-fetched))
|
||||
(rx/catch on-fetch-profile-exception)))))
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
[app.config :as cf]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.media :as di]
|
||||
[app.main.data.profile :as dp]
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.router :as rt]
|
||||
@@ -142,8 +143,9 @@
|
||||
|
||||
(defn update-member-role
|
||||
[{:keys [role member-id] :as params}]
|
||||
(dm/assert! (uuid? member-id))
|
||||
(dm/assert! (contains? ctt/valid-roles role))
|
||||
|
||||
(assert (uuid? member-id))
|
||||
(assert (contains? ctt/valid-roles role))
|
||||
|
||||
(ptk/reify ::update-member-role
|
||||
ptk/WatchEvent
|
||||
@@ -152,13 +154,13 @@
|
||||
params (assoc params :team-id team-id)]
|
||||
(->> (rp/cmd! :update-team-member-role params)
|
||||
(rx/mapcat (fn [_]
|
||||
(rx/of (fetch-members team-id)
|
||||
(rx/of (dp/refresh-profile)
|
||||
(fetch-members team-id)
|
||||
(fetch-teams)
|
||||
(ptk/data-event ::ev/event
|
||||
{::ev/name "update-team-member-role"
|
||||
:team-id team-id
|
||||
:role role
|
||||
:member-id member-id})))))))))
|
||||
(ev/event {::ev/name "update-team-member-role"
|
||||
:team-id team-id
|
||||
:role role
|
||||
:member-id member-id})))))))))
|
||||
|
||||
(defn delete-member
|
||||
[{:keys [member-id] :as params}]
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
[:> team-members-page* {:team team :profile profile}]
|
||||
|
||||
:dashboard-invitations
|
||||
[:> team-invitations-page* {:team team}]
|
||||
[:> team-invitations-page* {:team team :profile profile}]
|
||||
|
||||
:dashboard-webhooks
|
||||
[:> webhooks-page* {:team team}]
|
||||
|
||||
@@ -27,7 +27,11 @@
|
||||
[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 :refer [subscription-sidebar* menu-team-icon* get-subscription-type]]
|
||||
[app.main.ui.dashboard.subscription :refer [subscription-sidebar*
|
||||
menu-team-icon*
|
||||
dashboard-cta*
|
||||
show-subscription-dashboard-banner?
|
||||
get-subscription-type]]
|
||||
[app.main.ui.dashboard.team-form]
|
||||
[app.main.ui.icons :as i :refer [icon-xref]]
|
||||
[app.util.dom :as dom]
|
||||
@@ -838,7 +842,9 @@
|
||||
|
||||
[:*
|
||||
(when (contains? cf/flags :subscriptions)
|
||||
[:> subscription-sidebar* {:profile profile}])
|
||||
(if (show-subscription-dashboard-banner? profile)
|
||||
[:> dashboard-cta* {:profile profile}]
|
||||
[:> subscription-sidebar* {:profile profile}]))
|
||||
|
||||
;; TODO remove this block when subscriptions is full implemented
|
||||
(when (contains? cf/flags :subscriptions-old)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
(defn get-subscription-type
|
||||
[{:keys [type status] :as subscription}]
|
||||
(if (and subscription (not (contains? #{"unpaid" "canceled"} status)))
|
||||
(if (and subscription (:type subscription) (not (contains? #{"unpaid" "canceled"} status)))
|
||||
type
|
||||
"professional"))
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
[:> cta-power-up*
|
||||
{: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", subscription-href)
|
||||
:bottom-description (tr "subscription.dashboard.power-up.professional.bottom-text" subscription-href)
|
||||
:has-dropdown true}]
|
||||
|
||||
"unlimited"
|
||||
@@ -69,13 +69,13 @@
|
||||
[:> cta-power-up*
|
||||
{: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)
|
||||
: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.your-subscription")
|
||||
:top-description (tr "subscription.dashboard.power-up.unlimited-plan")
|
||||
:bottom-description (tr "subscription.dashboard.power-up.unlimited.bottom-text", subscription-href)
|
||||
:bottom-description (tr "subscription.dashboard.power-up.unlimited.bottom-text" subscription-href)
|
||||
:has-dropdown true}])
|
||||
|
||||
"enterprise"
|
||||
@@ -147,71 +147,74 @@
|
||||
[:span {:class (stl/css :item-name)} (tr "subscription.workspace.header.menu.option.power-up")]]))
|
||||
|
||||
(mf/defc members-cta*
|
||||
[{:keys [banner-is-expanded team]}]
|
||||
(let [subscription (:subscription team)
|
||||
[]
|
||||
[:> cta* {:class (stl/css :members-cta)
|
||||
:title (tr "subscription.dashboard.unlimited-members-extra-editors-cta-title")}
|
||||
[:> i18n/tr-html*
|
||||
{:tag-name "span"
|
||||
:class (stl/css :cta-message)
|
||||
:content (tr "subscription.dashboard.unlimited-members-extra-editors-cta-text")}]])
|
||||
|
||||
(mf/defc dashboard-cta*
|
||||
[{:keys [profile]}]
|
||||
(let [subscription (-> profile :props :subscription)
|
||||
subscription-type (get-subscription-type subscription)
|
||||
is-owner (-> team :permissions :is-owner)
|
||||
|
||||
email-owner (:email (some #(when (:is-owner %) %) (:members team)))
|
||||
mail-to-owner (str "<a href=\"" "mailto:" email-owner "\">" email-owner "</a>")
|
||||
go-to-subscription (dm/str (u/join cf/public-uri "#/settings/subscriptions"))
|
||||
seats (or (:seats subscription) 0)
|
||||
editors (count (filterv :can-edit (:members team)))
|
||||
|
||||
link
|
||||
(if is-owner
|
||||
go-to-subscription
|
||||
mail-to-owner)
|
||||
|
||||
seats (:quantity subscription)
|
||||
editors (count (:editors subscription))
|
||||
cta-title
|
||||
(cond
|
||||
(and (= "professional" subscription-type) (>= editors 8))
|
||||
(tr "subscription.dashboard.cta.professional-plan-designed")
|
||||
(= "professional" subscription-type)
|
||||
(tr "subscription.dashboard.professional-dashboard-cta-title" editors)
|
||||
|
||||
(and (= "unlimited" subscription-type) (< seats editors))
|
||||
(tr "subscription.dashboard.cta.unlimited-many-editors" seats editors))
|
||||
(= "unlimited" subscription-type)
|
||||
(tr "subscription.dashboard.unlimited-dashboard-cta-title" seats editors))
|
||||
|
||||
cta-message
|
||||
(cond
|
||||
(and (= "professional" subscription-type) (>= editors 8) is-owner)
|
||||
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-owner" link)
|
||||
(= "professional" subscription-type)
|
||||
(tr "subscription.dashboard.professional-dashboard-cta-upgrade-owner" go-to-subscription)
|
||||
|
||||
(and (= "professional" subscription-type) (>= editors 8) (not is-owner))
|
||||
(tr "subscription.dashboard.cta.upgrade-to-unlimited-enterprise-member" link)
|
||||
(= "unlimited" subscription-type)
|
||||
(tr "subscription.dashboard.unlimited-dashboard-cta-upgrade-owner" go-to-subscription))]
|
||||
|
||||
(and (= "unlimited" subscription-type) (< seats editors) is-owner)
|
||||
(tr "subscription.dashboard.cta.upgrade-more-seats-owner" link)
|
||||
|
||||
(and (= "unlimited" subscription-type) (< seats editors) (not is-owner))
|
||||
(tr "subscription.dashboard.cta.upgrade-more-seats-member" link))]
|
||||
|
||||
[:> cta* {:class (stl/css-case ::members-cta-full-width banner-is-expanded :members-cta (not banner-is-expanded)) :title cta-title}
|
||||
[:> cta* {:class (stl/css :dashboard-cta) :title cta-title}
|
||||
[:> i18n/tr-html*
|
||||
{:tag-name "span"
|
||||
:class (stl/css :cta-message)
|
||||
:content cta-message}]]))
|
||||
|
||||
(defn show-subscription-members-main-banner?
|
||||
[team]
|
||||
(let [subscription-type (get-subscription-type (:subscription team))
|
||||
seats (-> team :subscription :seats)
|
||||
editors (count (filter :can-edit (:members team)))]
|
||||
(defn show-subscription-dashboard-banner?
|
||||
[profile]
|
||||
(let [subscription (-> profile :props :subscription)
|
||||
subscription-type (get-subscription-type subscription)
|
||||
seats (:quantity subscription)
|
||||
editors (count (:editors subscription))]
|
||||
|
||||
(or
|
||||
(and (= subscription-type "professional")
|
||||
(> editors 8))
|
||||
(and
|
||||
(= subscription-type "unlimited")
|
||||
(>= editors 8)
|
||||
(< seats editors)))))
|
||||
(or
|
||||
;; common: seats < 25 and diff >= 4
|
||||
(and (< seats 25)
|
||||
(>= (- editors seats) 4))
|
||||
;; special: reached 25+ editors, seats < 25 and there is overuse
|
||||
(and (< seats 25)
|
||||
(>= editors 25)
|
||||
(> editors seats)))))))
|
||||
|
||||
(defn show-subscription-members-small-banner?
|
||||
[team]
|
||||
(let [subscription-type (get-subscription-type (:subscription team))
|
||||
seats (-> team :subscription :seats)
|
||||
editors (count (filterv :can-edit (:members team)))]
|
||||
(or
|
||||
(and (= subscription-type "professional")
|
||||
(= editors 8))
|
||||
(and (= subscription-type "unlimited")
|
||||
(< editors 8)
|
||||
(< seats editors)))))
|
||||
(defn show-subscription-members-banner?
|
||||
[team profile]
|
||||
(let [subscription (:subscription team)
|
||||
subscription-type (get-subscription-type subscription)
|
||||
seats (:seats subscription)
|
||||
editors (count (-> profile :props :subscription :editors))
|
||||
is-owner (-> team :permissions :is-owner)]
|
||||
(and
|
||||
is-owner
|
||||
(= subscription-type "unlimited")
|
||||
;; common: seats < 25 and diff >= 4 between editors/seats and there is overuse
|
||||
(and (< seats 25)
|
||||
(>= (- editors seats) 4)))))
|
||||
|
||||
@@ -143,16 +143,21 @@
|
||||
.members-cta {
|
||||
height: fit-content;
|
||||
margin-block-start: var(--sp-s);
|
||||
margin-inline-start: $s-68;
|
||||
max-width: $s-200;
|
||||
margin-inline-start: $s-52;
|
||||
max-width: $s-220;
|
||||
|
||||
.cta-title {
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.members-cta-full-width {
|
||||
max-width: $s-1000;
|
||||
.dashboard-cta {
|
||||
height: fit-content;
|
||||
margin: var(--sp-l);
|
||||
|
||||
.cta-title {
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-message {
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
[app.main.ui.dashboard.change-owner]
|
||||
[app.main.ui.dashboard.subscription :refer [team*
|
||||
members-cta*
|
||||
show-subscription-members-main-banner?
|
||||
show-subscription-members-small-banner?]]
|
||||
show-subscription-members-banner?]]
|
||||
[app.main.ui.dashboard.team-form]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||
[app.main.ui.icons :as i]
|
||||
@@ -267,8 +266,7 @@
|
||||
[:span {:class (stl/css :you)} (tr "labels.you")])]
|
||||
[:div {:class (stl/css :member-email)} (:email member)]]]))
|
||||
|
||||
(mf/defc rol-info
|
||||
{::mf/props :obj}
|
||||
(mf/defc rol-info*
|
||||
[{:keys [member team on-set-admin on-set-editor on-set-owner on-set-viewer profile]}]
|
||||
(let [member-is-owner (:is-owner member)
|
||||
member-is-admin (and (:is-admin member) (not member-is-owner))
|
||||
@@ -283,13 +281,15 @@
|
||||
is-you (= (:id profile) (:id member))
|
||||
|
||||
can-change-rol (or is-owner is-admin)
|
||||
not-superior (or (and (not member-is-owner) is-admin) (and can-change-rol (or member-is-admin member-is-editor member-is-viewer)))
|
||||
not-superior (or (and (not member-is-owner) is-admin)
|
||||
(and can-change-rol (or member-is-admin member-is-editor member-is-viewer)))
|
||||
|
||||
role (cond
|
||||
member-is-owner "labels.owner"
|
||||
member-is-admin "labels.admin"
|
||||
member-is-editor "labels.editor"
|
||||
:else "labels.viewer")
|
||||
|
||||
on-show (mf/use-fn #(reset! show? true))
|
||||
on-hide (mf/use-fn #(reset! show? false))]
|
||||
[:*
|
||||
@@ -321,8 +321,7 @@
|
||||
:class (stl/css :rol-dropdown-item)}
|
||||
(tr "labels.owner")])]]]))
|
||||
|
||||
(mf/defc member-actions
|
||||
{::mf/props :obj}
|
||||
(mf/defc member-actions*
|
||||
[{:keys [member team on-delete on-leave profile]}]
|
||||
(let [is-owner? (:is-owner member)
|
||||
owner? (dm/get-in team [:permissions :is-owner])
|
||||
@@ -471,20 +470,20 @@
|
||||
[:& member-info {:member member :profile profile}]]
|
||||
|
||||
[:div {:class (stl/css :table-field :field-roles)}
|
||||
[:& rol-info {:member member
|
||||
:team team
|
||||
:on-set-admin on-set-admin
|
||||
:on-set-editor on-set-editor
|
||||
:on-set-viewer on-set-viewer
|
||||
:on-set-owner on-set-owner
|
||||
:profile profile}]]
|
||||
[:> rol-info* {:member member
|
||||
:team team
|
||||
:on-set-admin on-set-admin
|
||||
:on-set-editor on-set-editor
|
||||
:on-set-viewer on-set-viewer
|
||||
:on-set-owner on-set-owner
|
||||
:profile profile}]]
|
||||
|
||||
[:div {:class (stl/css :table-field :field-actions)}
|
||||
[:& member-actions {:member member
|
||||
:profile profile
|
||||
:team team
|
||||
:on-delete on-delete
|
||||
:on-leave on-leave'}]]]))
|
||||
[:> member-actions* {:member member
|
||||
:profile profile
|
||||
:team team
|
||||
:on-delete on-delete
|
||||
:on-leave on-leave'}]]]))
|
||||
|
||||
(mf/defc team-members*
|
||||
{::mf/props :obj
|
||||
@@ -541,21 +540,15 @@
|
||||
|
||||
[:*
|
||||
[:& header {:section :dashboard-team-members :team team}]
|
||||
[:section {:class (stl/css-case
|
||||
:dashboard-container true
|
||||
:dashboard-team-members true
|
||||
:dashboard-top-cta (show-subscription-members-main-banner? team))}
|
||||
(when (and (contains? cfg/flags :subscriptions)
|
||||
(show-subscription-members-main-banner? team))
|
||||
[:> members-cta* {:banner-is-expanded true :team team}])
|
||||
[:section {:class (stl/css :dashboard-container :dashboard-team-members)}
|
||||
|
||||
[:> team-members*
|
||||
{:profile profile
|
||||
:team team}]
|
||||
|
||||
(when (and
|
||||
(contains? cfg/flags :subscriptions)
|
||||
(show-subscription-members-small-banner? team))
|
||||
[:> members-cta* {:banner-is-expanded false :team team}])]])
|
||||
(when (and (contains? cfg/flags :subscriptions)
|
||||
(show-subscription-members-banner? team profile))
|
||||
[:> members-cta* {:team team}])]])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; INVITATIONS SECTION
|
||||
@@ -803,7 +796,7 @@
|
||||
|
||||
(mf/defc team-invitations-page*
|
||||
{::mf/props :obj}
|
||||
[{:keys [team]}]
|
||||
[{:keys [team profile]}]
|
||||
|
||||
(mf/with-effect [team]
|
||||
(dom/set-html-title
|
||||
@@ -818,16 +811,13 @@
|
||||
[:*
|
||||
[:& header {:section :dashboard-team-invitations
|
||||
:team team}]
|
||||
[:section {:class (stl/css-case
|
||||
:dashboard-team-invitations true
|
||||
:dashboard-top-cta (show-subscription-members-main-banner? team))}
|
||||
(when (and (contains? cfg/flags :subscriptions)
|
||||
(show-subscription-members-main-banner? team))
|
||||
[:> members-cta* {:banner-is-expanded true :team team}])
|
||||
[:section {:class (stl/css :dashboard-team-invitations)}
|
||||
|
||||
[:> invitation-section* {:team team}]
|
||||
|
||||
(when (and (contains? cfg/flags :subscriptions)
|
||||
(show-subscription-members-small-banner? team))
|
||||
[:> members-cta* {:banner-is-expanded false :team team}])]])
|
||||
(show-subscription-members-banner? team profile))
|
||||
[:> members-cta* {:team team}])]])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; WEBHOOKS SECTION
|
||||
|
||||
@@ -300,11 +300,6 @@
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.dashboard-team-invitations.dashboard-top-cta {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.invitations {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
|
||||
@@ -8,15 +8,13 @@
|
||||
[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.components.forms :as fm]
|
||||
[app.main.ui.dashboard.subscription :refer [get-subscription-type]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[beicon.v2.core :as rx]
|
||||
[app.util.i18n :as i18n :refer [tr c]]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
@@ -54,14 +52,15 @@
|
||||
: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])])
|
||||
(def ^:private schema:seats-form
|
||||
(defn schema:seats-form [min-editors]
|
||||
[:map {:title "SeatsForm"}
|
||||
[:min-members [::sm/number {:min 1 :max 9999}]]])
|
||||
[:min-members [::sm/number {:min min-editors
|
||||
:max 9999}]]])
|
||||
|
||||
(mf/defc subscribe-management-dialog
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :management-dialog}
|
||||
[{:keys [subscription-type current-subscription teams subscribe-to-trial]}]
|
||||
[{:keys [subscription-type current-subscription editors subscribe-to-trial]}]
|
||||
|
||||
(let [subscription-name (if subscribe-to-trial
|
||||
(if (= subscription-type "unlimited")
|
||||
@@ -71,19 +70,24 @@
|
||||
"professional" (tr "subscription.settings.professional")
|
||||
"unlimited" (tr "subscription.settings.unlimited")
|
||||
"enterprise" (tr "subscription.settings.enterprise")))
|
||||
initial (mf/with-memo []
|
||||
{:min-members (or (some->> teams (map :total-editors) (apply max)) 1)})
|
||||
form (fm/use-form :schema schema:seats-form
|
||||
min-editors (or (count editors) 1)
|
||||
initial (mf/with-memo [min-editors]
|
||||
{:min-members min-editors})
|
||||
form (fm/use-form :schema (schema:seats-form min-editors)
|
||||
:initial initial)
|
||||
submit-in-progress* (mf/use-state false)
|
||||
subscribe-to-unlimited (mf/use-fn
|
||||
(fn [form]
|
||||
(let [data (:clean-data @form)
|
||||
return-url (-> (rt/get-current-href) (rt/encode-url))
|
||||
href (dm/str "payments/subscriptions/create?type=unlimited&quantity=" (:min-members data) "&returnUrl=" return-url)]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "create-trial-subscription"
|
||||
:type "unlimited"
|
||||
:quantity (:min-members data)})
|
||||
(rt/nav-raw :href href)))))
|
||||
(when (not @submit-in-progress*)
|
||||
(reset! submit-in-progress* true)
|
||||
(let [data (:clean-data @form)
|
||||
return-url (-> (rt/get-current-href) (rt/encode-url))
|
||||
href (dm/str "payments/subscriptions/create?type=unlimited&quantity=" (:min-members data) "&returnUrl=" return-url)]
|
||||
(reset! form nil)
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "create-trial-subscription"
|
||||
:type "unlimited"
|
||||
:quantity (:min-members data)})
|
||||
(rt/nav-raw :href href))))))
|
||||
|
||||
subscribe-to-enterprise (mf/use-fn
|
||||
(fn []
|
||||
@@ -106,7 +110,14 @@
|
||||
handle-close-dialog (mf/use-fn
|
||||
(fn []
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "close-subscription-modal"}))
|
||||
(modal/hide!)))]
|
||||
(modal/hide!)))
|
||||
|
||||
show-editors-list* (mf/use-state false)
|
||||
show-editors-list (deref show-editors-list*)
|
||||
handle-click (mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! show-editors-list* not)))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog)}
|
||||
@@ -115,15 +126,19 @@
|
||||
(tr "subscription.settings.management.dialog.title" 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 teams]
|
||||
[:li {:key (dm/str (:id team)) :class (stl/css :team-name)}
|
||||
(:name team) (tr "subscription.settings.management.dialog.members" (:total-editors team))])]]
|
||||
[:div {:class (stl/css :modal-text)}
|
||||
(tr "subscription.settings.management.dialog.no-teams")])
|
||||
(when (seq editors)
|
||||
[:* [:p {:class (stl/css :editors-text)}
|
||||
(tr "subscription.settings.management.dialog.currently-editors-title" (c (count editors)))]
|
||||
[:button {:class (stl/css :cta-button :show-editors-button) :on-click handle-click}
|
||||
(tr "subscription.settings.management.dialog.editors")
|
||||
[:span {:class (stl/css :icon-dropdown)} i/arrow]]
|
||||
(when show-editors-list
|
||||
[:*
|
||||
[:p {:class (stl/css :editors-text :editors-list-warning)}
|
||||
(tr "subscription.settings.management.dialog.editors-explanation")]
|
||||
[:ul {:class (stl/css :editors-list)}
|
||||
(for [editor editors]
|
||||
[:li {:key (dm/str (:id editor)) :class (stl/css :team-name)} "- " (:name editor)])]])])
|
||||
|
||||
(when (and
|
||||
(or (and (= subscription-type "professional") (contains? #{"unlimited" "enterprise"} (:type current-subscription)))
|
||||
@@ -138,8 +153,6 @@
|
||||
[:& fm/form {:on-submit subscribe-to-unlimited
|
||||
:class (stl/css :seats-form)
|
||||
:form form}
|
||||
[: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 :fields-row)}
|
||||
@@ -149,7 +162,7 @@
|
||||
:label ""
|
||||
:class (stl/css :input-field)}]]
|
||||
[:div {:class (stl/css :editors-cost)}
|
||||
[:span {:class (stl/css :modal-text-small)}
|
||||
[:span {:class (stl/css :modal-text-medium)}
|
||||
(when (> (get-in @form [:clean-data :min-members]) 25)
|
||||
[:> i18n/tr-html*
|
||||
{:class (stl/css :modal-text-cap)
|
||||
@@ -160,9 +173,16 @@
|
||||
:tag-name "span"
|
||||
:content (tr "subscription.settings.management.dialog.price-month"
|
||||
(* 7 (or (get-in @form [:clean-data :min-members]) 0)))}]]
|
||||
[:span {:class (stl/css :modal-text-small)}
|
||||
[:span {:class (stl/css :modal-text-medium)}
|
||||
(tr "subscription.settings.management.dialog.payment-explanation")]]]
|
||||
|
||||
(when (get-in @form [:errors :min-members])
|
||||
[:div {:class (stl/css :error-message)}
|
||||
(tr "subscription.settings.management.dialog.input-error")])
|
||||
|
||||
[:div {:class (stl/css :unlimited-capped-warning)}
|
||||
(tr "subscription.settings.management.dialog.unlimited-capped-warning")]
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
[:input
|
||||
@@ -214,6 +234,8 @@
|
||||
|
||||
[:div {:class (stl/css :modal-end)}
|
||||
[:div {:class (stl/css :modal-title)} (tr "subscription.settings.sucess.dialog.title" subscription-name)]
|
||||
(when (not= subscription-name "professional")
|
||||
[:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.success.dialog.thanks" 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")]
|
||||
|
||||
@@ -229,9 +251,6 @@
|
||||
(let [route (mf/deref refs/route)
|
||||
authenticated? (da/is-authenticated? profile)
|
||||
|
||||
teams* (mf/use-state nil)
|
||||
teams (deref teams*)
|
||||
|
||||
params-subscription
|
||||
(-> route :params :query :subscription)
|
||||
|
||||
@@ -246,6 +265,9 @@
|
||||
success-modal-is-trial?
|
||||
(-> route :params :query :trial)
|
||||
|
||||
subscription-editors
|
||||
(-> profile :props :subscription :editors)
|
||||
|
||||
subscription
|
||||
(-> profile :props :subscription)
|
||||
|
||||
@@ -282,7 +304,7 @@
|
||||
|
||||
open-subscription-modal
|
||||
(mf/use-fn
|
||||
(mf/deps teams)
|
||||
(mf/deps subscription-editors)
|
||||
(fn [subscription-type current-subscription]
|
||||
(st/emit! (ev/event {::ev/name "open-subscription-modal"
|
||||
::ev/origin "settings:in-app"}))
|
||||
@@ -290,12 +312,7 @@
|
||||
(modal/show :management-dialog
|
||||
{:subscription-type subscription-type
|
||||
:current-subscription current-subscription
|
||||
:teams teams :subscribe-to-trial (not subscription)}))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(->> (rp/cmd! :get-owned-teams)
|
||||
(rx/subs! (fn [teams]
|
||||
(reset! teams* teams)))))
|
||||
:editors subscription-editors :subscribe-to-trial (not (:type subscription))}))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(dom/set-html-title (tr "subscription.labels")))
|
||||
@@ -313,8 +330,8 @@
|
||||
"unlimited"
|
||||
"enterprise")
|
||||
:current-subscription subscription
|
||||
:teams teams
|
||||
:subscribe-to-trial (not subscription)})
|
||||
:editors subscription-editors
|
||||
:subscribe-to-trial (not (:type subscription))})
|
||||
(rt/nav :settings-subscription {} {::rt/replace true}))
|
||||
|
||||
^boolean show-subscription-success-modal?
|
||||
@@ -339,18 +356,18 @@
|
||||
(case subscription-type
|
||||
"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-autosave")]}]
|
||||
:benefits [(tr "subscription.settings.professional.storage-benefit"),
|
||||
(tr "subscription.settings.professional.autosave-benefit"),
|
||||
(tr "subscription.settings.professional.teams-editors-benefit")]}]
|
||||
|
||||
"unlimited"
|
||||
(if subscription-is-trial?
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.unlimited-trial")
|
||||
:card-title-icon i/character-u
|
||||
: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-autosave")]
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professional-benefits"),
|
||||
:benefits [(tr "subscription.settings.unlimited.storage-benefit")
|
||||
(tr "subscription.settings.unlimited.autosave-benefit"),
|
||||
(tr "subscription.settings.unlimited.bill")]
|
||||
:cta-text (tr "subscription.settings.manage-your-subscription")
|
||||
:cta-link go-to-payments
|
||||
:cta-text-trial (tr "subscription.settings.add-payment-to-continue")
|
||||
@@ -360,9 +377,9 @@
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.unlimited")
|
||||
:card-title-icon i/character-u
|
||||
:benefits-title (tr "subscription.settings.benefits.all-unlimited-benefits")
|
||||
:benefits [(tr "subscription.settings.unlimited.teams"),
|
||||
(tr "subscription.settings.unlimited.bill"),
|
||||
(tr "subscription.settings.unlimited.storage-autosave")]
|
||||
:benefits [(tr "subscription.settings.unlimited.storage-benefit"),
|
||||
(tr "subscription.settings.unlimited.autosave-benefit"),
|
||||
(tr "subscription.settings.unlimited.bill")]
|
||||
:cta-text (tr "subscription.settings.manage-your-subscription")
|
||||
:cta-link go-to-payments
|
||||
:editors (-> profile :props :subscription :quantity)}])
|
||||
@@ -371,18 +388,20 @@
|
||||
(if subscription-is-trial?
|
||||
[:> plan-card* {:card-title (tr "subscription.settings.enterprise-trial")
|
||||
:card-title-icon i/character-e
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professional-benefits")
|
||||
:benefits [(tr "subscription.settings.enterprise.unlimited-storage"),
|
||||
(tr "subscription.settings.enterprise.capped-bill"),
|
||||
(tr "subscription.settings.enterprise.autosave")]
|
||||
:benefits-title (tr "subscription.settings.benefits.all-unlimited-benefits"),
|
||||
:benefits [(tr "subscription.settings.enterprise.unlimited-storage-benefit"),
|
||||
(tr "subscription.settings.enterprise.autosave"),
|
||||
(tr "subscription.settings.enterprise.capped-bill")]
|
||||
: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.enterprise")
|
||||
:card-title-icon i/character-e
|
||||
:benefits-title (tr "subscription.settings.benefits.all-professional-benefits")
|
||||
:benefits [(tr "subscription.settings.enterprise.unlimited-storage"),
|
||||
(tr "subscription.settings.enterprise.capped-bill"),
|
||||
(tr "subscription.settings.enterprise.autosave")]
|
||||
:benefits-title (tr "subscription.settings.benefits.all-unlimited-benefits"),
|
||||
:benefits [(tr "subscription.settings.enterprise.unlimited-storage-benefit"),
|
||||
(tr "subscription.settings.enterprise.autosave"),
|
||||
(tr "subscription.settings.enterprise.capped-bill")]
|
||||
:cta-text (tr "subscription.settings.manage-your-subscription")
|
||||
:cta-link go-to-payments}]))
|
||||
|
||||
@@ -404,9 +423,9 @@
|
||||
[:> 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-autosave")]
|
||||
:benefits [(tr "subscription.settings.professional.storage-benefit"),
|
||||
(tr "subscription.settings.professional.autosave-benefit"),
|
||||
(tr "subscription.settings.professional.teams-editors-benefit")]
|
||||
:cta-text (tr "subscription.settings.subscribe")
|
||||
:cta-link #(open-subscription-modal "professional")
|
||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||
@@ -418,10 +437,10 @@
|
||||
:price-value "$7"
|
||||
:price-period (tr "subscription.settings.price-editor-month")
|
||||
: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-autosave")]
|
||||
:cta-text (if subscription (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
|
||||
:benefits [(tr "subscription.settings.unlimited.storage-benefit"),
|
||||
(tr "subscription.settings.unlimited.autosave-benefit"),
|
||||
(tr "subscription.settings.unlimited.bill")]
|
||||
:cta-text (if (:type subscription) (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
|
||||
:cta-link #(open-subscription-modal "unlimited" subscription)
|
||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||
:cta-link-with-icon go-to-pricing-page}])
|
||||
@@ -432,10 +451,10 @@
|
||||
:price-value "$950"
|
||||
:price-period (tr "subscription.settings.price-organization-month")
|
||||
:benefits-title (tr "subscription.settings.benefits.all-unlimited-benefits")
|
||||
:benefits [(tr "subscription.settings.enterprise.unlimited-storage"),
|
||||
(tr "subscription.settings.enterprise.capped-bill"),
|
||||
(tr "subscription.settings.enterprise.autosave")]
|
||||
:cta-text (if subscription (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
|
||||
:benefits [(tr "subscription.settings.enterprise.unlimited-storage-benefit"),
|
||||
(tr "subscription.settings.enterprise.autosave"),
|
||||
(tr "subscription.settings.enterprise.capped-bill")]
|
||||
:cta-text (if (:type subscription) (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
|
||||
:cta-link #(open-subscription-modal "enterprise" subscription)
|
||||
:cta-text-with-icon (tr "subscription.settings.more-information")
|
||||
:cta-link-with-icon go-to-pricing-page}])]]]))
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
.title-section {
|
||||
@include t.use-typography("title-large");
|
||||
color: var(--color-foreground-primary);
|
||||
margin-block-end: var(--sp-l);
|
||||
margin-block-end: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.plan-section-title {
|
||||
@@ -81,7 +81,7 @@
|
||||
.plan-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-block-end: var(--sp-s);
|
||||
margin-block-end: var(--sp-m);
|
||||
}
|
||||
|
||||
.plan-card-title-container {
|
||||
@@ -135,12 +135,13 @@
|
||||
}
|
||||
|
||||
.other-subscriptions {
|
||||
margin-block-start: $s-36;
|
||||
margin-block-start: $s-52;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
@include t.use-typography("body-medium");
|
||||
@include buttonStyle;
|
||||
align-items: center;
|
||||
color: var(--color-accent-tertiary);
|
||||
display: flex;
|
||||
margin-block-start: var(--sp-m);
|
||||
@@ -155,6 +156,10 @@
|
||||
margin-inline-start: var(--sp-xs);
|
||||
}
|
||||
|
||||
.icon-dropdown svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.bottom-link {
|
||||
margin-block-start: var(--sp-xs);
|
||||
}
|
||||
@@ -168,11 +173,11 @@
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
max-height: initial;
|
||||
min-width: $s-520;
|
||||
min-width: $s-548;
|
||||
}
|
||||
|
||||
.modal-dialog.subscription-success {
|
||||
min-width: $s-612;
|
||||
min-width: $s-648;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@@ -191,12 +196,8 @@
|
||||
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-text-medium {
|
||||
@include t.use-typography("body-medium");
|
||||
}
|
||||
|
||||
.modal-text-cap {
|
||||
@@ -207,7 +208,7 @@
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.modal-text-small strong,
|
||||
.modal-text-medium strong,
|
||||
.text-strikethrough strong,
|
||||
.modal-text-cap strong {
|
||||
font-weight: $fw700;
|
||||
@@ -260,11 +261,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
.teams-list {
|
||||
list-style-position: inside;
|
||||
list-style-type: disc;
|
||||
.editors-text {
|
||||
@include t.use-typography("body-medium");
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.editors-list-warning {
|
||||
margin-inline-start: var(--sp-xl);
|
||||
margin-block: var(--sp-s);
|
||||
}
|
||||
|
||||
.editors-list {
|
||||
@include t.use-typography("body-medium");
|
||||
list-style-position: inside;
|
||||
list-style-type: none;
|
||||
margin-inline-start: var(--sp-xl);
|
||||
margin-block: var(--sp-xxl);
|
||||
max-height: $s-216;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@@ -274,11 +285,14 @@
|
||||
width: $s-80;
|
||||
}
|
||||
|
||||
.editors-label {
|
||||
margin-block-start: var(--sp-xxl);
|
||||
.error-message {
|
||||
@include t.use-typography("body-small");
|
||||
color: var(--color-foreground-error);
|
||||
margin-block-start: $s-8;
|
||||
}
|
||||
|
||||
.editors-wrapper {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: var(--sp-xl);
|
||||
margin-block-start: var(--sp-l);
|
||||
@@ -288,3 +302,16 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.unlimited-capped-warning {
|
||||
@include t.use-typography("body-small");
|
||||
background-color: var(--color-background-tertiary);
|
||||
border-radius: var(--sp-s);
|
||||
margin-block-start: $s-40;
|
||||
padding-block: var(--sp-s);
|
||||
padding-inline: var(--sp-m);
|
||||
}
|
||||
|
||||
.show-editors-button {
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
@@ -501,16 +501,16 @@
|
||||
profile (mf/deref refs/profile)
|
||||
|
||||
auth-error? (= type :authentication)
|
||||
not-found? (= type :not-found)
|
||||
|
||||
authenticated?
|
||||
(is-authenticated? profile)
|
||||
|
||||
request-access?
|
||||
(and
|
||||
(or (= type :not-found) auth-error?)
|
||||
(or workspace? dashboard? view?)
|
||||
(or (:file-id info)
|
||||
(:team-id info)))]
|
||||
(or (some? (:file-id info))
|
||||
(some? (:team-id info))))]
|
||||
|
||||
(mf/with-effect [params info]
|
||||
(when-not (:loaded info)
|
||||
@@ -518,25 +518,26 @@
|
||||
(rx/subs! (partial reset! info*)
|
||||
(partial reset! info* {:loaded true})))))
|
||||
|
||||
(if (and auth-error? (not authenticated?))
|
||||
[:> context-wrapper*
|
||||
{:is-workspace workspace?
|
||||
:is-dashboard dashboard?
|
||||
:is-viewer view?
|
||||
:profile profile}
|
||||
[:> login-dialog* {}]]
|
||||
|
||||
(when (get info :loaded false)
|
||||
(if request-access?
|
||||
[:> context-wrapper* {:is-workspace workspace?
|
||||
:is-dashboard dashboard?
|
||||
:is-viewer view?
|
||||
:profile profile}
|
||||
[:> request-access* {:file-id (:file-id info)
|
||||
:team-id (:team-id info)
|
||||
:is-default (:team-default info)
|
||||
:profile profile
|
||||
:is-workspace workspace?}]]
|
||||
|
||||
[:> exception-section* props])))))
|
||||
(if (or auth-error? not-found?)
|
||||
(if (not authenticated?)
|
||||
[:> context-wrapper*
|
||||
{:is-workspace workspace?
|
||||
:is-dashboard dashboard?
|
||||
:is-viewer view?
|
||||
:profile profile}
|
||||
[:> login-dialog* {}]]
|
||||
(when (get info :loaded false)
|
||||
(if request-access?
|
||||
[:> context-wrapper* {:is-workspace workspace?
|
||||
:is-dashboard dashboard?
|
||||
:is-viewer view?
|
||||
:profile profile}
|
||||
[:> request-access* {:file-id (:file-id info)
|
||||
:team-id (:team-id info)
|
||||
:is-default (:team-default info)
|
||||
:profile profile
|
||||
:is-workspace workspace?}]]
|
||||
[:> exception-section* props])))
|
||||
|
||||
[:> exception-section* props])))
|
||||
|
||||
Reference in New Issue
Block a user