diff --git a/frontend/resources/images/assets/nitrate-success-dark.svg b/frontend/resources/images/assets/nitrate-success-dark.svg
new file mode 100644
index 0000000000..239364da9b
--- /dev/null
+++ b/frontend/resources/images/assets/nitrate-success-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/resources/images/assets/nitrate-success-light.svg b/frontend/resources/images/assets/nitrate-success-light.svg
new file mode 100644
index 0000000000..e1343cbc4c
--- /dev/null
+++ b/frontend/resources/images/assets/nitrate-success-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/resources/images/nitrate-success.png b/frontend/resources/images/nitrate-success.png
new file mode 100644
index 0000000000..9d8e1b7853
Binary files /dev/null and b/frontend/resources/images/nitrate-success.png differ
diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js
index c6396f99bc..da6f277ea5 100644
--- a/frontend/scripts/_helpers.js
+++ b/frontend/scripts/_helpers.js
@@ -369,6 +369,9 @@ async function generateSvgSprite(files, prefix) {
mode: {
symbol: { inline: true },
},
+ shape: {
+ transform: [],
+ },
});
for (let path of files) {
diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs
index 9663111d98..0e368ff25a 100644
--- a/frontend/src/app/main/ui/dashboard/sidebar.cljs
+++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs
@@ -40,7 +40,9 @@
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg*]]
[app.main.ui.icons :as deprecated-icon]
+ [app.main.ui.nitrate.nitrate-code-activation-modal]
[app.main.ui.nitrate.nitrate-form]
+ [app.main.ui.nitrate.nitrate-activation-success-modal]
[app.util.dom :as dom]
[app.util.dom.dnd :as dnd]
[app.util.i18n :as i18n :refer [tr]]
diff --git a/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs b/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs
index e744cfe292..ee74bf98b9 100644
--- a/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs
+++ b/frontend/src/app/main/ui/ds/foundations/assets/raw_svg.cljs
@@ -21,6 +21,8 @@
(def ^:svg-id logo-subscription "logo-subscription")
(def ^:svg-id logo-subscription-light "logo-subscription-light")
(def ^:svg-id nitrate-welcome "nitrate-welcome")
+(def ^:svg-id nitrate-success-dark "nitrate-success-dark")
+(def ^:svg-id nitrate-success-light "nitrate-success-light")
(def ^:svg-id marketing-arrows "marketing-arrows")
(def ^:svg-id marketing-exchange "marketing-exchange")
(def ^:svg-id marketing-file "marketing-file")
@@ -39,3 +41,4 @@
(assert (contains? raw-svg-list id) "invalid raw svg id")
[:> "svg" props
[:use {:href (dm/str "#asset-" id)}]])
+
diff --git a/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs b/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs
new file mode 100644
index 0000000000..da0a0746ee
--- /dev/null
+++ b/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs
@@ -0,0 +1,85 @@
+;; This Source Code Form is subject to the terms of the Mozilla Public
+;; License, v. 2.0. If a copy of the MPL was not distributed with this
+;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;;
+;; Copyright (c) KALEIDOS INC
+
+(ns app.main.ui.nitrate.nitrate-activation-success-modal
+ (:require-macros [app.main.style :as stl])
+ (:require
+ [app.common.data.macros :as dm]
+ [app.common.time :as ct]
+ [app.main.data.modal :as modal]
+ [app.main.data.nitrate :as dnt]
+ [app.main.refs :as refs]
+ [app.main.ui.ds.buttons.button :refer [button*]]
+ [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]]
+ [app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg*]]
+ [app.util.i18n :refer [tr]]
+ [app.util.theme :as theme]
+ [beicon.v2.core :as rx]
+ [rumext.v2 :as mf]))
+
+(mf/defc nitrate-activation-success-modal*
+ {::mf/register modal/components
+ ::mf/register-as :nitrate-activation-success
+ ::mf/wrap-props true}
+ [connectivity]
+
+ (let [profile (mf/deref refs/profile)
+ profile-theme (get profile :theme theme/default)
+ system-theme* (mf/use-state theme/get-system-theme)
+
+ light?
+ (mf/with-memo [profile-theme (deref system-theme*)]
+ (let [resolved (cond
+ (= profile-theme "light") "light"
+ (= profile-theme "system") (deref system-theme*)
+ :else "dark")]
+ (= resolved "light")))
+
+ svg-id (if light? "nitrate-success-light" "nitrate-success-dark")
+
+ cancel-at (dm/get-in connectivity [:subscription :cancel-at])
+ date-str (when cancel-at
+ (ct/format-inst cancel-at "d MMMM, yyyy"))
+
+ on-create-org
+ (mf/use-fn
+ (fn []
+ (modal/hide!)
+ (dnt/go-to-nitrate-cc-create-org)))]
+
+ (mf/with-effect []
+ (let [s (->> (rx/from-event (.. js/window (matchMedia "(prefers-color-scheme: dark)")) "change")
+ (rx/map #(if (.-matches %) "dark" "light"))
+ (rx/subs! #(reset! system-theme* %)))]
+ (fn [] (rx/dispose! s))))
+
+ [:div {:class (stl/css :modal-overlay)}
+ [:div {:class (stl/css :modal-dialog)}
+ [:button {:class (stl/css :close-btn) :on-click modal/hide!}
+ [:> icon* {:icon-id "close"
+ :size "m"}]]
+
+ [:div {:class (stl/css :modal-content)}
+ [:div {:class (stl/css :modal-start)}
+ [:> raw-svg* {:id svg-id}]]
+
+ [:div {:class (stl/css :modal-end)}
+ [:div {:class (stl/css :modal-title)}
+ "You are Business Nitrate!"]
+
+ [:p {:class (stl/css :modal-text-primary)}
+ (dm/str "Your plan is active until " (or date-str "—") ".")]
+
+ [:p {:class (stl/css :modal-text)}
+ "You can manage your subscription anytime from the Subscription page in your account settings."]
+
+ [:p {:class (stl/css :modal-text)}
+ "Enjoy your plan!"]
+
+ [:> button* {:variant "primary"
+ :on-click on-create-org
+ :class (stl/css :modal-button)}
+ "Create organization"]]]]]))
diff --git a/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.scss b/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.scss
new file mode 100644
index 0000000000..72c5dfc339
--- /dev/null
+++ b/frontend/src/app/main/ui/nitrate/nitrate_activation_success_modal.scss
@@ -0,0 +1,79 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) KALEIDOS INC
+
+@use "refactor/common-refactor.scss" as deprecated;
+@use "ds/typography.scss" as t;
+@use "ds/_borders.scss" as *;
+@use "ds/spacing.scss" as *;
+@use "ds/_sizes.scss" as *;
+@use "ds/_utils.scss" as *;
+
+.modal-overlay {
+ @extend %modal-overlay-base;
+
+ z-index: var(--z-index-notifications);
+}
+
+.modal-dialog {
+ @extend %modal-container-base;
+
+ max-block-size: initial;
+ min-inline-size: px2rem(608);
+ max-inline-size: px2rem(608);
+ padding: var(--sp-xxxl);
+}
+
+.close-btn {
+ @extend %modal-close-btn-base;
+}
+
+.modal-content {
+ display: flex;
+ gap: $sz-40;
+}
+
+.modal-start {
+ display: flex;
+ justify-content: center;
+ min-inline-size: $sz-224;
+
+ svg {
+ inline-size: 100%;
+ block-size: auto;
+ }
+
+ @media (width <= 640px) {
+ display: none;
+ }
+}
+
+.modal-end {
+ color: var(--color-foreground-secondary);
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-m);
+}
+
+.modal-title {
+ @include t.use-typography("title-large");
+
+ color: var(--modal-title-foreground-color);
+}
+
+.modal-text-primary {
+ @include t.use-typography("body-large");
+
+ color: var(--color-foreground-primary);
+}
+
+.modal-text {
+ @include t.use-typography("body-large");
+}
+
+.modal-button {
+ margin-block-start: var(--sp-s);
+ align-self: flex-start;
+}
diff --git a/frontend/src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs b/frontend/src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs
new file mode 100644
index 0000000000..2afa6d7cc3
--- /dev/null
+++ b/frontend/src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs
@@ -0,0 +1,69 @@
+;; This Source Code Form is subject to the terms of the Mozilla Public
+;; License, v. 2.0. If a copy of the MPL was not distributed with this
+;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;;
+;; Copyright (c) KALEIDOS INC
+
+(ns app.main.ui.nitrate.nitrate-code-activation-modal
+ (:require-macros [app.main.style :as stl])
+ (:require
+ [app.main.data.modal :as modal]
+ [app.main.store :as st]
+ [app.main.ui.components.forms :as fm]
+ [app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
+ [app.main.ui.ds.foundations.assets.icon :as i]
+ [app.util.i18n :refer [tr]]
+ [rumext.v2 :as mf]))
+
+(def ^:private schema:activate-form
+ [:map {:title "ActivateForm"}
+ [:activation-code [:string {:min 1}]]])
+
+(mf/defc nitrate-code-activation-modal*
+ {::mf/register modal/components
+ ::mf/register-as :nitrate-code-activation}
+ [_props]
+ (let [initial (mf/with-memo []
+ {:activation-code ""})
+ form (fm/use-form :schema schema:activate-form
+ :initial initial)
+
+ on-accept
+ (mf/use-fn
+ (mf/deps form)
+ (fn [_]
+ ;; TODO: dispatch activation action with (-> @form :clean-data :activation-code)
+ (modal/hide!)
+ ;; TODO: remove, only for flow testing
+ (st/emit! (modal/show {:type :nitrate-activation-success}))))]
+
+ [:div {:class (stl/css :modal-overlay)}
+ [:div {:class (stl/css :modal-dialog)}
+ [:> icon-button* {:variant "ghost"
+ :class (stl/css :close-btn)
+ :aria-label (tr "labels.close")
+ :on-click modal/hide!
+ :icon i/close}]
+
+ [:div {:class (stl/css :modal-header)}
+ [:h2 {:class (stl/css :modal-title)} "Activate Nitrate"]]
+
+ [:div {:class (stl/css :modal-content)}
+ [:& fm/form {:form form :on-submit on-accept}
+ [:& fm/input {:name :activation-code
+ :auto-focus? true
+ :label "Enter your activation code"
+ :type "text"
+ :placeholder "XXXX-XXXXX-XXXXX-XXXXX"}]]
+ [:input
+ {:type "button"
+ :class (stl/css-case :accept-btn true
+ :global/disabled (not (:valid @form)))
+ :disabled (not (:valid @form))
+ :value "Activate"
+ :on-click on-accept}]
+ [:div {:class (stl/css :footer-text)}
+ "Need a code? Contact us: "
+ [:a {:class (stl/css :link)
+ :href "mailto:sales@nitrate.com"}
+ "sales@nitrate.com"]]]]]))
diff --git a/frontend/src/app/main/ui/nitrate/nitrate_code_activation_modal.scss b/frontend/src/app/main/ui/nitrate/nitrate_code_activation_modal.scss
new file mode 100644
index 0000000000..4fbe799df4
--- /dev/null
+++ b/frontend/src/app/main/ui/nitrate/nitrate_code_activation_modal.scss
@@ -0,0 +1,65 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) KALEIDOS INC
+
+@use "refactor/common-refactor.scss" as deprecated;
+@use "ds/typography.scss" as t;
+@use "ds/spacing.scss" as *;
+@use "ds/_sizes.scss" as *;
+
+.close-btn {
+ @extend %modal-close-btn-base;
+}
+
+.modal-overlay {
+ @extend %modal-overlay-base;
+
+ z-index: var(--z-index-notifications);
+}
+
+.modal-dialog {
+ @extend %modal-container-base;
+
+ min-inline-size: px2rem(480);
+ padding: var(--sp-xxxl);
+}
+
+.modal-title {
+ @include t.use-typography("title-large");
+
+ color: var(--modal-title-foreground-color);
+ margin-block-end: var(--sp-xxxl);
+}
+
+.modal-content {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-m);
+ color: var(--color-foreground-secondary);
+}
+
+.modal-description {
+ @include t.use-typography("body-large");
+}
+
+.action-buttons {
+ @extend %modal-action-btns;
+}
+
+.accept-btn {
+ @extend %modal-accept-btn;
+ width: 100%;
+}
+
+.footer-text {
+ @include t.use-typography("body-medium");
+
+ color: var(--color-foreground-secondary);
+ margin-block-start: var(--sp-xxxl);
+}
+
+.link {
+ color: var(--color-accent-primary);
+}
diff --git a/frontend/src/app/main/ui/nitrate/nitrate_form.cljs b/frontend/src/app/main/ui/nitrate/nitrate_form.cljs
index ef3bc46d38..c6a5aae89e 100644
--- a/frontend/src/app/main/ui/nitrate/nitrate_form.cljs
+++ b/frontend/src/app/main/ui/nitrate/nitrate_form.cljs
@@ -11,6 +11,7 @@
[app.main.data.modal :as modal]
[app.main.data.nitrate :as dnt]
[app.main.refs :as refs]
+ [app.main.store :as st]
[app.main.ui.components.forms :as fm]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]]
@@ -37,7 +38,12 @@
(mf/use-fn
(mf/deps form)
(fn []
- (dnt/go-to-buy-nitrate-license (-> @form :clean-data :subscription name))))]
+ (dnt/go-to-buy-nitrate-license (-> @form :clean-data :subscription name))))
+
+ on-activate-click
+ (mf/use-fn
+ (fn []
+ (st/emit! (modal/show {:type :nitrate-code-activation}))))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog :subscription-success)}
@@ -80,6 +86,10 @@
[:div {:class (stl/css :modal-text-small :modal-info)}
"Cancel anytime before your next billing cycle."]]]
+ [:p {:class (stl/css :modal-text-medium)}
+ "Have an activation code? " [:a {:class (stl/css :link)
+ :on-click on-activate-click}
+ "Enter activation code"]]
[:p {:class (stl/css :modal-text-medium)}
[:a {:class (stl/css :link) :href dnt/go-to-subscription-url}
@@ -92,6 +102,13 @@
"Contact us to try Nitrate for 14 days:")]
[:p {:class (stl/css :modal-text-large)}
[:a {:class (stl/css :link) :href "mailto:sales@penpot.app"}
- "sales@penpot.app"]]])]]]]))
+ "sales@penpot.app"]]
+ [:div {:class (stl/css :activation-code)}
+ [:p {:class (stl/css :modal-text-large)}
+ "Have an activation code?"]
+ [:p {:class (stl/css :modal-text-large)}
+ [:a {:class (stl/css :link)
+ :on-click on-activate-click}
+ "Enter activation code"]]]])]]]]))
diff --git a/frontend/src/app/main/ui/nitrate/nitrate_form.scss b/frontend/src/app/main/ui/nitrate/nitrate_form.scss
index d21a24c26c..69c33d87d5 100644
--- a/frontend/src/app/main/ui/nitrate/nitrate_form.scss
+++ b/frontend/src/app/main/ui/nitrate/nitrate_form.scss
@@ -107,6 +107,10 @@
color: var(--color-foreground-primary);
}
+.activation-code {
+ margin-block-start: var(--sp-xxxl);
+}
+
.link {
color: var(--color-accent-primary);
}
diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs
index e22a65ff01..ccf88b31e5 100644
--- a/frontend/src/app/main/ui/settings/subscription.cljs
+++ b/frontend/src/app/main/ui/settings/subscription.cljs
@@ -18,6 +18,7 @@
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg*]]
+ [app.main.ui.nitrate.nitrate-activation-success-modal]
[app.main.ui.notifications.badge :refer [badge-notification]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr c]]
@@ -37,6 +38,7 @@
cta-link-trial
cta-text-with-icon
cta-link-with-icon
+ show-activation-by-code
editors
recommended
show-button-cta]}]
@@ -66,6 +68,14 @@
[: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
@@ -75,14 +85,10 @@
[: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 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 show-activation-by-code
+ [:button {:class (stl/css :cta-button :activate-by-code)
+ :on-click #(st/emit! (modal/show {:type :nitrate-code-activation}))}
+ "Enter activation code"])])
(defn- make-management-form-schema [min-editors]
[:map {:title "SeatsForm"}
@@ -360,36 +366,6 @@
:value (tr "labels.close")
:on-click handle-close-dialog}]]]]]]))
-(mf/defc nitrate-success-dialog
- {::mf/register modal/components
- ::mf/register-as :nitrate-success}
- []
- ;; TODO add translations for this texts when we have the definitive ones
- (let [profile (mf/deref refs/profile)]
-
- [:div {:class (stl/css :modal-overlay)}
- [:div {:class (stl/css :modal-dialog :subscription-success)}
- [:button {:class (stl/css :close-btn) :on-click modal/hide!}
- [:> icon* {:icon-id "close"
- :size "m"}]]
- [:div {:class (stl/css :modal-success-content)}
- [:div {:class (stl/css :modal-start)}
- [:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription")}]]
-
- [:div {:class (stl/css :modal-end)}
- [:div {:class (stl/css :modal-title)}
- "You are Business Nitrate!"]
- [: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 "CREATE ORGANIZATION"
- :on-click dnt/go-to-nitrate-cc-create-org}]]]]]]))
(mf/defc subscription-page*
[{:keys [profile]}]
@@ -498,7 +474,7 @@
^boolean show-subscription-success-modal?
(st/emit!
(if (= params-subscription "subscribed-to-penpot-nitrate")
- (modal/show :nitrate-success {})
+ (modal/show :nitrate-activation-success {})
(modal/show :subscription-success
{:subscription-name (if (= params-subscription "subscribed-to-penpot-unlimited")
(if (= success-modal-is-trial? "true")
@@ -658,6 +634,7 @@
:cta-link #(open-subscription-modal "nitrate" subscription)
:cta-text-with-icon (tr "subscription.settings.more-information")
:cta-link-with-icon go-to-pricing-page
+ :show-activation-by-code true
:show-button-cta (not nitrate-license)}])]]]))