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]))
(mf/defc plan-card*
{::mf/wrap [mf/memo]}
[{:keys [card-title
card-title-icon
price-value price-period
cancel-at
benefits-title benefits
cta-text
cta-link
cta-text-trial
cta-link-trial
cta-text-with-icon
cta-link-with-icon
cta-text cta-link
cta-text-trial cta-link-trial
cta-text-with-icon cta-link-with-icon
code-action
editors
recommended
show-button-cta]}]
[:div {:class (stl/css-case :plan-card true
:plan-card-highlight recommended)}
[:div {:class (stl/css :plan-card-header)}
[:div {:class (stl/css :plan-card-title-container)}
(when card-title-icon
[:> icon* {:icon-id card-title-icon
:class (stl/css :plan-title-icon)
:size "s"}])
[:h4 {:class (stl/css :plan-card-title)} card-title]
(when recommended
[:& badge-notification {:content (tr "subscription.settings.recommended")
:size :small
:is-focus true}])
(when editors [:span {:class (stl/css :plan-editors)} (tr "subscription.settings.editors" editors)])]
(when (and price-value price-period)
[:div {:class (stl/css :plan-price)}
[:span {:class (stl/css :plan-price-value)} price-value]
[:span {:class (stl/css :plan-price-period)} " / " price-period]])
(when cancel-at
[:div {:class (stl/css :plan-cancel)}
[:span {:class (stl/css :plan-cancel-date)} cancel-at]])]
(when benefits-title [:h5 {:class (stl/css :benefits-title)} benefits-title])
[:ul {:class (stl/css :benefits-list)}
(for [benefit benefits]
[:li {:key (dm/str benefit) :class (stl/css :benefit)} "- " benefit])]
(when (and cta-link cta-text show-button-cta)
[:> button* {:variant "primary"
:type "button"
:class (stl/css-case :bottom-button (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])
(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
[:> icon* {:icon-id "open-link"
:size "s"}]])
(when (and cta-link cta-text (not show-button-cta))
[:button {:class (stl/css-case :cta-button true
:bottom-link (not (or (and cta-link-trial cta-text-trial) code-action)))
:on-click cta-link} cta-text])
(when code-action
[:button {:class (stl/css-case :cta-button true
:activate-by-code (= code-action :activate)
: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"))])])
(let [has-trial? (and cta-link-trial cta-text-trial)
has-cta-with-icon? (and cta-link-with-icon cta-text-with-icon)
has-cta-button (and cta-link cta-text show-button-cta)
has-cta-link (and cta-link cta-text (not show-button-cta))]
[:div {:class (stl/css-case :plan-card true
:plan-card-highlight recommended)}
[:div {:class (stl/css :plan-card-header)}
[:div {:class (stl/css :plan-card-title-container)}
(when card-title-icon
[:> icon* {:icon-id card-title-icon
:class (stl/css :plan-title-icon)
:size "s"}])
[:h4 {:class (stl/css :plan-card-title)} card-title]
(when recommended
[:& badge-notification {:content (tr "subscription.settings.recommended")
:size :small
:is-focus true}])
(when editors
[:span {:class (stl/css :plan-editors)} (tr "subscription.settings.editors" editors)])]
(when (and price-value price-period)
[:div {:class (stl/css :plan-price)}
[:span {:class (stl/css :plan-price-value)} price-value]
[:span {:class (stl/css :plan-price-period)} " / " price-period]])
(when cancel-at
[:div {:class (stl/css :plan-cancel)}
[:span {:class (stl/css :plan-cancel-date)} cancel-at]])]
(when benefits-title
[:h5 {:class (stl/css :benefits-title)} benefits-title])
[:ul {:class (stl/css :benefits-list)}
(for [benefit benefits]
[:li {:key (dm/str benefit) :class (stl/css :benefit)} "- " benefit])]
(when has-cta-button
[:> button* {:variant "primary"
:type "button"
:class (stl/css-case :bottom-button (not has-trial?))
:on-click cta-link} cta-text])
(when has-trial?
[:button {:class (stl/css :cta-button :bottom-link)
:on-click cta-link-trial} cta-text-trial])
(when has-cta-with-icon?
[:button {:class (stl/css :cta-button :more-info)
:on-click cta-link-with-icon} cta-text-with-icon
[:> icon* {:icon-id "open-link"
:size "s"}]])
(when has-cta-link
[:button {:class (stl/css-case :cta-button true
:bottom-link (not (or has-trial? code-action)))
:on-click cta-link} cta-text])
(when code-action
[:button {:class (stl/css-case :cta-button true
:activate-by-code (= code-action :activate)
: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]
[:map {:title "SeatsForm"}
@ -114,14 +153,7 @@
(deref unlimited-modal-step*)
subscription-name
(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")))
(get-subscription-name subscription-type subscribe-to-trial)
min-editors
(if (seq editors) (count editors) 1)
@ -184,18 +216,6 @@
(st/emit! (ptk/event ::ev/event {::ev/name "close-subscription-modal"}))
(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
(mf/use-fn
(mf/deps current-subscription unlimited-modal-step*)
@ -225,20 +245,7 @@
[:div {:class (stl/css :modal-content)}
(when (and (seq editors) (not= unlimited-modal-step 2))
[:* [: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)])]])])
[:> editors-section* {:editors editors}])
(when (and
(or (and (= subscription-type "professional")
@ -265,20 +272,20 @@
:class (stl/css :input-field)}]]
[:div {:class (stl/css :editors-cost)}
[: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*
{:class (stl/css :modal-text-cap)
:tag-name "span"
:content (tr "subscription.settings.management.dialog.price-month" "175")}])
[:> 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"
: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)}
(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)}
(tr "subscription.settings.management.dialog.input-error")])
@ -527,15 +534,15 @@
:benefits ["Loren ipsum",
"Loren ipsum",
"Loren ipsum"]
:cta-text-with-icon (when (:licenses connectivity) "Control Center")
:cta-link-with-icon (when (:licenses connectivity) dnt/go-to-nitrate-cc)
:cta-text (if (:licenses connectivity)
:cta-text-with-icon (when (not (:manual nitrate-license)) "Control Center")
:cta-link-with-icon (when (not (:manual nitrate-license)) dnt/go-to-nitrate-cc)
:cta-text (if (and (:licenses connectivity) (not (:manual nitrate-license)))
(tr "subscription.settings.manage-your-subscription")
(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
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
"professional"
[:> plan-card* {:card-title (tr "subscription.settings.professional")
@ -615,7 +622,11 @@
(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-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-link-with-icon go-to-pricing-page}])
@ -769,8 +780,7 @@
[:> icon* {:icon-id "close"
:size "m"}]]
[: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-text-medium)}
"When you downgrade:"]

View File

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