From 3141f67cd71ac199054e0d64a71ac9b9add577d5 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 10 Mar 2026 13:37:40 +0100 Subject: [PATCH] :sparkles: Add subscription info for nitrate --- backend/src/app/nitrate.clj | 87 ++++++++++++++++--- frontend/src/app/main/data/nitrate.cljs | 7 ++ .../src/app/main/ui/dashboard/sidebar.cljs | 4 +- .../app/main/ui/dashboard/subscription.cljs | 5 +- .../app/main/ui/settings/subscription.cljs | 11 +-- 5 files changed, 90 insertions(+), 24 deletions(-) diff --git a/backend/src/app/nitrate.clj b/backend/src/app/nitrate.clj index 0c1e0303d8..3036ecfcbe 100644 --- a/backend/src/app/nitrate.clj +++ b/backend/src/app/nitrate.clj @@ -3,6 +3,8 @@ (:require [app.common.logging :as l] [app.common.schema :as sm] + [app.common.schema.generators :as sg] + [app.common.time :as ct] [app.config :as cf] [app.http.client :as http] [app.rpc :as-alias rpc] @@ -83,9 +85,73 @@ [:id ::sm/text] [:name ::sm/text]]) -(def ^:private schema:user - [:map - [:valid ::sm/boolean]]) +;; TODO Unify with schemas on backend/src/app/http/management.clj +(def ^:private schema:timestamp + (sm/type-schema + {:type ::timestamp + :pred ct/inst? + :type-properties + {:title "inst" + :description "The same as :app.common.time/inst but encodes to epoch" + :error/message "should be an instant" + :gen/gen (->> (sg/small-int) + (sg/fmap (fn [v] (ct/inst v)))) + :decode/string ct/inst + :encode/string inst-ms + :decode/json ct/inst + :encode/json inst-ms}})) + +(def ^:private schema:subscription + [:map {:title "Subscription"} + [:id ::sm/text] + [:customer-id ::sm/text] + [:type [:enum + "unlimited" + "professional" + "enterprise" + "nitrate"]] + [:status [:enum + "active" + "canceled" + "incomplete" + "incomplete_expired" + "past_due" + "paused" + "trialing" + "unpaid"]] + + [:billing-period [:enum + "month" + "day" + "week" + "year"]] + [:quantity :int] + [:description [:maybe ::sm/text]] + [:created-at schema:timestamp] + [:start-date [:maybe schema:timestamp]] + [:ended-at [:maybe schema:timestamp]] + [:trial-end [:maybe schema:timestamp]] + [:trial-start [:maybe schema:timestamp]] + [:cancel-at [:maybe schema:timestamp]] + [:canceled-at [:maybe schema:timestamp]] + [:current-period-end [:maybe schema:timestamp]] + [:current-period-start [:maybe schema:timestamp]] + [:cancel-at-period-end :boolean] + + [:cancellation-details + [:map {:title "CancellationDetails"} + [:comment [:maybe ::sm/text]] + [:reason [:maybe ::sm/text]] + [:feedback [:maybe + [:enum + "customer_service" + "low_quality" + "missing_feature" + "other" + "switched_service" + "too_complex" + "too_expensive" + "unused"]]]]]]) (def ^:private schema:connectivity [:map @@ -96,10 +162,10 @@ (let [baseuri (cf/get :nitrate-backend-uri)] (request-to-nitrate cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params))) -(defn- is-valid-user +(defn- get-subscription [cfg {:keys [profile-id] :as params}] (let [baseuri (cf/get :nitrate-backend-uri)] - (request-to-nitrate cfg :get (str baseuri "/api/users/" (str profile-id)) schema:user params))) + (request-to-nitrate cfg :get (str baseuri "/api/subscriptions/" (str profile-id)) schema:subscription params))) (defn- get-connectivity [cfg params] @@ -113,9 +179,9 @@ (defmethod ig/init-key ::client [_ cfg] (when (contains? cf/flags :nitrate) - {:get-team-org (partial get-team-org cfg) - :is-valid-user (partial is-valid-user cfg) - :connectivity (partial get-connectivity cfg)})) + {:get-team-org (partial get-team-org cfg) + :get-subscription (partial get-subscription cfg) + :connectivity (partial get-connectivity cfg)})) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UTILS @@ -125,9 +191,8 @@ (defn add-nitrate-licence-to-profile [cfg profile] (try - (let [nitrate-licence (call cfg :is-valid-user {:profile-id (:id profile)})] - (assoc-in profile [:props :nitrate-license] - (select-keys nitrate-licence [:valid :created-at]))) + (let [subscription (call cfg :get-subscription {:profile-id (:id profile)})] + (assoc profile :subscription subscription)) (catch Throwable cause (l/error :hint "failed to get nitrate licence" :profile-id (:id profile) diff --git a/frontend/src/app/main/data/nitrate.cljs b/frontend/src/app/main/data/nitrate.cljs index c356a3c436..e118b1ec40 100644 --- a/frontend/src/app/main/data/nitrate.cljs +++ b/frontend/src/app/main/data/nitrate.cljs @@ -39,4 +39,11 @@ (def go-to-subscription-url (u/join cf/public-uri "#/settings/subscriptions")) +(defn is-valid-license? + [profile] + (and (contains? cf/flags :nitrate) + ;; Possible values: "active" "canceled" "incomplete" "incomplete_expired" "past_due" "paused" "trialing" "unpaid" + (contains? #{"active" "past_due" "trialing"} + (dm/get-in profile [:subscription :status])))) + diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index efb0c241f0..040e127696 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -302,7 +302,7 @@ on-create-org-click (mf/use-fn (fn [] - (if (dm/get-in profile [:props :nitrate-license :valid]) + (if (dnt/is-valid-license? profile) (dnt/go-to-nitrate-cc) (st/emit! (dnt/show-nitrate-popup :nitrate-form)))))] @@ -547,7 +547,7 @@ on-create-org-click (mf/use-fn (fn [] - (if (dm/get-in profile [:props :nitrate-license :valid]) + (if (dnt/is-valid-license? profile) (dnt/go-to-nitrate-cc) (st/emit! (dnt/show-nitrate-popup :nitrate-form)))))] (if empty? diff --git a/frontend/src/app/main/ui/dashboard/subscription.cljs b/frontend/src/app/main/ui/dashboard/subscription.cljs index 00536d48ad..8749305c1d 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.cljs +++ b/frontend/src/app/main/ui/dashboard/subscription.cljs @@ -120,10 +120,7 @@ (mf/defc nitrate-sidebar* {::mf/props :obj} [{:keys [profile teams]}] - (let [nitrate-license (dm/get-in profile [:props :nitrate-license]) - nitrate? (and (contains? cf/flags :nitrate) - (:valid nitrate-license)) - + (let [nitrate? (dnt/is-valid-license? profile) orgs (mf/with-memo [teams] (let [orgs (->> teams vals diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index 929f0d8789..65c1eccf95 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -389,13 +389,10 @@ (mf/defc subscription-page* [{:keys [profile]}] - (let [route (mf/deref refs/route) - authenticated? (da/is-authenticated? profile) - - nitrate-license (dm/get-in profile [:props :nitrate-license]) - - nitrate? (and (contains? cf/flags :nitrate) - (:valid nitrate-license)) + (let [route (mf/deref refs/route) + authenticated? (da/is-authenticated? profile) + nitrate-license (:subscription profile) + nitrate? (dnt/is-valid-license? profile) params-subscription (-> route :params :query :subscription)