Refactor subscriptions page

This commit is contained in:
Marina López 2026-05-06 15:54:06 +02:00 committed by Francis Santiago
parent 6e186143d5
commit bc13dfcf9e
2 changed files with 134 additions and 128 deletions

View File

@ -27,75 +27,114 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc plan-card* (mf/defc plan-card*
{::mf/wrap [mf/memo]}
[{:keys [card-title [{:keys [card-title
card-title-icon card-title-icon
price-value price-period price-value price-period
cancel-at cancel-at
benefits-title benefits benefits-title benefits
cta-text cta-text cta-link
cta-link cta-text-trial cta-link-trial
cta-text-trial cta-text-with-icon cta-link-with-icon
cta-link-trial
cta-text-with-icon
cta-link-with-icon
code-action code-action
editors editors
recommended recommended
show-button-cta]}] show-button-cta]}]
[:div {:class (stl/css-case :plan-card true (let [has-trial? (and cta-link-trial cta-text-trial)
:plan-card-highlight recommended)} has-cta-with-icon? (and cta-link-with-icon cta-text-with-icon)
[:div {:class (stl/css :plan-card-header)} has-cta-button (and cta-link cta-text show-button-cta)
[:div {:class (stl/css :plan-card-title-container)} has-cta-link (and cta-link cta-text (not show-button-cta))]
(when card-title-icon [:div {:class (stl/css-case :plan-card true
[:> icon* {:icon-id card-title-icon :plan-card-highlight recommended)}
:class (stl/css :plan-title-icon) [:div {:class (stl/css :plan-card-header)}
:size "s"}]) [:div {:class (stl/css :plan-card-title-container)}
[:h4 {:class (stl/css :plan-card-title)} card-title] (when card-title-icon
(when recommended [:> icon* {:icon-id card-title-icon
[:& badge-notification {:content (tr "subscription.settings.recommended") :class (stl/css :plan-title-icon)
:size :small :size "s"}])
:is-focus true}]) [:h4 {:class (stl/css :plan-card-title)} card-title]
(when editors [:span {:class (stl/css :plan-editors)} (tr "subscription.settings.editors" editors)])] (when recommended
(when (and price-value price-period) [:& badge-notification {:content (tr "subscription.settings.recommended")
[:div {:class (stl/css :plan-price)} :size :small
[:span {:class (stl/css :plan-price-value)} price-value] :is-focus true}])
[:span {:class (stl/css :plan-price-period)} " / " price-period]]) (when editors
(when cancel-at [:span {:class (stl/css :plan-editors)} (tr "subscription.settings.editors" editors)])]
[:div {:class (stl/css :plan-cancel)} (when (and price-value price-period)
[:span {:class (stl/css :plan-cancel-date)} cancel-at]])] [:div {:class (stl/css :plan-price)}
(when benefits-title [:h5 {:class (stl/css :benefits-title)} benefits-title]) [:span {:class (stl/css :plan-price-value)} price-value]
[:ul {:class (stl/css :benefits-list)} [:span {:class (stl/css :plan-price-period)} " / " price-period]])
(for [benefit benefits] (when cancel-at
[:li {:key (dm/str benefit) :class (stl/css :benefit)} "- " benefit])] [:div {:class (stl/css :plan-cancel)}
(when (and cta-link cta-text show-button-cta) [:span {:class (stl/css :plan-cancel-date)} cancel-at]])]
[:> button* {:variant "primary" (when benefits-title
:type "button" [:h5 {:class (stl/css :benefits-title)} benefits-title])
:class (stl/css-case :bottom-button (not (and cta-link-trial cta-text-trial))) [:ul {:class (stl/css :benefits-list)}
:on-click cta-link} cta-text]) (for [benefit benefits]
(when (and cta-link-trial cta-text-trial) [:li {:key (dm/str benefit) :class (stl/css :benefit)} "- " benefit])]
[:button {:class (stl/css :cta-button :bottom-link)
:on-click cta-link-trial} cta-text-trial]) (when has-cta-button
(when (and cta-link-with-icon cta-text-with-icon) [:> button* {:variant "primary"
[:button {:class (stl/css :cta-button :more-info) :type "button"
:on-click cta-link-with-icon} cta-text-with-icon :class (stl/css-case :bottom-button (not has-trial?))
[:> icon* {:icon-id "open-link" :on-click cta-link} cta-text])
:size "s"}]]) (when has-trial?
(when (and cta-link cta-text (not show-button-cta)) [:button {:class (stl/css :cta-button :bottom-link)
[:button {:class (stl/css-case :cta-button true :on-click cta-link-trial} cta-text-trial])
:bottom-link (not (or (and cta-link-trial cta-text-trial) code-action))) (when has-cta-with-icon?
:on-click cta-link} cta-text]) [:button {:class (stl/css :cta-button :more-info)
(when code-action :on-click cta-link-with-icon} cta-text-with-icon
[:button {:class (stl/css-case :cta-button true [:> icon* {:icon-id "open-link"
:activate-by-code (= code-action :activate) :size "s"}]])
:renew-by-code (= code-action :renovate) (when has-cta-link
:bottom-link (= code-action :renovate)) [:button {:class (stl/css-case :cta-button true
;; TODO add renovation modal :bottom-link (not (or has-trial? code-action)))
:on-click (when (= code-action :activate) :on-click cta-link} cta-text])
#(st/emit! (modal/show {:type :nitrate-code-activation})))} (when code-action
(if (= code-action :activate) [:button {:class (stl/css-case :cta-button true
(tr "subscription.settings.activate-by-code") :activate-by-code (= code-action :activate)
(tr "nitrate.subscription.settings.renew-with-code"))])]) :renew-by-code (= code-action :renovate)
:bottom-link (= code-action :renovate))
;; TODO add renovation modal
:on-click (when (= code-action :activate)
#(st/emit! (modal/show {:type :nitrate-code-activation})))}
(if (= code-action :activate)
(tr "subscription.settings.activate-by-code")
(tr "nitrate.subscription.settings.renew-with-code"))])]))
(defn- get-subscription-name [subscription-type subscribe-to-trial?]
(if subscribe-to-trial?
(if (= subscription-type "unlimited")
(tr "subscription.settings.unlimited-trial")
(tr "subscription.settings.enterprise-trial"))
(case subscription-type
"professional" (tr "subscription.settings.professional")
"unlimited" (tr "subscription.settings.unlimited")
"enterprise" (tr "subscription.settings.enterprise"))))
(mf/defc ^:private editors-section*
[{:keys [editors]}]
(let [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)))]
[:*
[: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")
[:> icon* {:icon-id (if show-editors-list i/arrow-up i/arrow-down)
:class (stl/css :icon-dropdown)
:size "s"}]]
(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)])]])]))
(defn- make-management-form-schema [min-editors] (defn- make-management-form-schema [min-editors]
[:map {:title "SeatsForm"} [:map {:title "SeatsForm"}
@ -114,14 +153,7 @@
(deref unlimited-modal-step*) (deref unlimited-modal-step*)
subscription-name subscription-name
(if subscribe-to-trial (get-subscription-name subscription-type subscribe-to-trial)
(if (= subscription-type "unlimited")
(tr "subscription.settings.unlimited-trial")
(tr "subscription.settings.enterprise-trial"))
(case subscription-type
"professional" (tr "subscription.settings.professional")
"unlimited" (tr "subscription.settings.unlimited")
"enterprise" (tr "subscription.settings.enterprise")))
min-editors min-editors
(if (seq editors) (count editors) 1) (if (seq editors) (count editors) 1)
@ -184,18 +216,6 @@
(st/emit! (ptk/event ::ev/event {::ev/name "close-subscription-modal"})) (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)))
on-submit on-submit
(mf/use-fn (mf/use-fn
(mf/deps current-subscription unlimited-modal-step*) (mf/deps current-subscription unlimited-modal-step*)
@ -225,20 +245,7 @@
[:div {:class (stl/css :modal-content)} [:div {:class (stl/css :modal-content)}
(when (and (seq editors) (not= unlimited-modal-step 2)) (when (and (seq editors) (not= unlimited-modal-step 2))
[:* [:p {:class (stl/css :editors-text)} [:> editors-section* {:editors editors}])
(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")
[:> icon* {:icon-id (if show-editors-list i/arrow-up i/arrow-down)
:class (stl/css :icon-dropdown)
:size "s"}]]
(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 (when (and
(or (and (= subscription-type "professional") (or (and (= subscription-type "professional")
@ -265,20 +272,20 @@
:class (stl/css :input-field)}]] :class (stl/css :input-field)}]]
[:div {:class (stl/css :editors-cost)} [:div {:class (stl/css :editors-cost)}
[:span {:class (stl/css :modal-text-medium)} [:span {:class (stl/css :modal-text-medium)}
(when (> (get-in @form [:clean-data :min-members]) 25) (when (> (dm/get-in @form [:clean-data :min-members]) 25)
[:> i18n/tr-html* [:> i18n/tr-html*
{:class (stl/css :modal-text-cap) {:class (stl/css :modal-text-cap)
:tag-name "span" :tag-name "span"
:content (tr "subscription.settings.management.dialog.price-month" "175")}]) :content (tr "subscription.settings.management.dialog.price-month" "175")}])
[:> i18n/tr-html* [:> i18n/tr-html*
{:class (stl/css-case :text-strikethrough (> (get-in @form [:clean-data :min-members]) 25)) {:class (stl/css-case :text-strikethrough (> (dm/get-in @form [:clean-data :min-members]) 25))
:tag-name "span" :tag-name "span"
:content (tr "subscription.settings.management.dialog.price-month" :content (tr "subscription.settings.management.dialog.price-month"
(* 7 (or (get-in @form [:clean-data :min-members]) 0)))}]] (* 7 (or (dm/get-in @form [:clean-data :min-members]) 0)))}]]
[:span {:class (stl/css :modal-text-medium)} [:span {:class (stl/css :modal-text-medium)}
(tr "subscription.settings.management.dialog.payment-explanation")]]] (tr "subscription.settings.management.dialog.payment-explanation")]]]
(when (get-in @form [:errors :min-members]) (when (dm/get-in @form [:errors :min-members])
[:div {:class (stl/css :error-message)} [:div {:class (stl/css :error-message)}
(tr "subscription.settings.management.dialog.input-error")]) (tr "subscription.settings.management.dialog.input-error")])
@ -527,15 +534,15 @@
:benefits ["Loren ipsum", :benefits ["Loren ipsum",
"Loren ipsum", "Loren ipsum",
"Loren ipsum"] "Loren ipsum"]
:cta-text-with-icon (when (:licenses connectivity) "Control Center") :cta-text-with-icon (when (not (:manual nitrate-license)) "Control Center")
:cta-link-with-icon (when (:licenses connectivity) dnt/go-to-nitrate-cc) :cta-link-with-icon (when (not (:manual nitrate-license)) dnt/go-to-nitrate-cc)
:cta-text (if (:licenses connectivity) :cta-text (if (and (:licenses connectivity) (not (:manual nitrate-license)))
(tr "subscription.settings.manage-your-subscription") (tr "subscription.settings.manage-your-subscription")
(tr "nitrate.subscription.settings.manual-cancel")) (tr "nitrate.subscription.settings.manual-cancel"))
:cta-link (if (:licenses connectivity) :cta-link (if (and (:licenses connectivity) (not (:manual nitrate-license)))
dnt/go-to-nitrate-billing dnt/go-to-nitrate-billing
open-cancel-contact-sales-modal) open-cancel-contact-sales-modal)
:code-action (when (and (not (:licenses connectivity)) (:manual nitrate-license)) :renovate)}] :code-action (when (:manual nitrate-license) :renovate)}]
(case subscription-type (case subscription-type
"professional" "professional"
[:> plan-card* {:card-title (tr "subscription.settings.professional") [:> plan-card* {:card-title (tr "subscription.settings.professional")
@ -615,7 +622,11 @@
(tr "subscription.settings.professional.autosave-benefit"), (tr "subscription.settings.professional.autosave-benefit"),
(tr "subscription.settings.professional.teams-editors-benefit")] (tr "subscription.settings.professional.teams-editors-benefit")]
:cta-text (tr "subscription.settings.subscribe") :cta-text (tr "subscription.settings.subscribe")
:cta-link #(open-subscription-modal "professional") :cta-link (if (and (contains? cf/flags :nitrate) nitrate? (= subscription-type "nitrate"))
(if (:licenses connectivity)
dnt/go-to-nitrate-billing
open-cancel-contact-sales-modal)
go-to-payments)
:cta-text-with-icon (tr "subscription.settings.more-information") :cta-text-with-icon (tr "subscription.settings.more-information")
:cta-link-with-icon go-to-pricing-page}]) :cta-link-with-icon go-to-pricing-page}])
@ -769,8 +780,7 @@
[:> icon* {:icon-id "close" [:> icon* {:icon-id "close"
:size "m"}]] :size "m"}]]
[:div {:class (stl/css :modal-title :subscription-title)} [:div {:class (stl/css :modal-title :subscription-title)}
(str "Switch to " subscription-type " plan?")] (dm/str "Switch to " subscription-type " plan?")]
[:div {:class (stl/css :modal-content)} [:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-text-medium)} [:div {:class (stl/css :modal-text-medium)}
"When you downgrade:"] "When you downgrade:"]

View File

@ -38,10 +38,6 @@
margin-block-start: var(--sp-s); margin-block-start: var(--sp-s);
} }
.membership.first {
margin-block-start: var(--sp-l);
}
.membership-date { .membership-date {
@include t.use-typography("body-small"); @include t.use-typography("body-small");
@ -109,11 +105,11 @@
border: 1.75px solid var(--color-foreground-primary); border: 1.75px solid var(--color-foreground-primary);
stroke-width: 2.25px; stroke-width: 2.25px;
padding: px2rem(3); padding: px2rem(3);
}
svg { .plan-title-icon svg {
block-size: var(--sp-m); block-size: var(--sp-m);
inline-size: var(--sp-m); inline-size: var(--sp-m);
}
} }
.plan-card-title, .plan-card-title,
@ -292,16 +288,16 @@
justify-content: center; justify-content: center;
max-inline-size: $sz-224; max-inline-size: $sz-224;
svg {
inline-size: 100%;
block-size: auto;
}
@media (width <= 992px) { @media (width <= 992px) {
display: none; display: none;
} }
} }
.modal-start svg {
inline-size: 100%;
block-size: auto;
}
.editors-text { .editors-text {
@include t.use-typography("body-medium"); @include t.use-typography("body-medium");
@ -363,21 +359,21 @@
} }
.radio-btns { .radio-btns {
label {
@include t.use-typography("body-large");
padding: 0;
display: flex;
align-items: center;
color: var(--color-foreground-secondary);
}
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0 0 var(--sp-xl) 0; padding-block-end: var(--sp-xl);
gap: var(--sp-s); gap: var(--sp-s);
} }
.radio-btns label {
@include t.use-typography("body-large");
padding: 0;
display: flex;
align-items: center;
color: var(--color-foreground-secondary);
}
.modal-contact-content { .modal-contact-content {
gap: var(--sp-xl); gap: var(--sp-xl);
} }