From 0bd6df4329e871354ac4d5a5b02615ea180b786b Mon Sep 17 00:00:00 2001 From: Adrian Date: Tue, 15 Aug 2023 21:12:05 +0300 Subject: [PATCH] initial commit --- .editorconfig | 12 ++ .github/workflows/reset.yml | 14 ++ .gitignore | 0 readme.md | 10 ++ template.rb | 59 +++++++++ .../app/avo/cards/active_subscriptions.rb | 9 ++ template/app/avo/cards/total_revenue.rb | 44 +++++++ template/app/avo/cards/users_count.rb | 9 ++ template/app/avo/dashboards/overview.rb | 30 +++++ template/app/avo/resources/invitation.rb | 21 +++ template/app/avo/resources/membership.rb | 23 ++++ template/app/avo/resources/team.rb | 24 ++++ template/app/avo/resources/user.rb | 24 ++++ .../controllers/avo/invitations_controller.rb | 4 + .../controllers/avo/memberships_controller.rb | 4 + .../app/controllers/avo/teams_controller.rb | 4 + .../app/controllers/avo/users_controller.rb | 4 + template/config/initializers/avo.rb | 122 ++++++++++++++++++ 18 files changed, 417 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/reset.yml create mode 100644 .gitignore create mode 100644 readme.md create mode 100644 template.rb create mode 100644 template/app/avo/cards/active_subscriptions.rb create mode 100644 template/app/avo/cards/total_revenue.rb create mode 100644 template/app/avo/cards/users_count.rb create mode 100644 template/app/avo/dashboards/overview.rb create mode 100644 template/app/avo/resources/invitation.rb create mode 100644 template/app/avo/resources/membership.rb create mode 100644 template/app/avo/resources/team.rb create mode 100644 template/app/avo/resources/user.rb create mode 100644 template/app/controllers/avo/invitations_controller.rb create mode 100644 template/app/controllers/avo/memberships_controller.rb create mode 100644 template/app/controllers/avo/teams_controller.rb create mode 100644 template/app/controllers/avo/users_controller.rb create mode 100644 template/config/initializers/avo.rb diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5acb96c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.key] +insert_final_newline = false diff --git a/.github/workflows/reset.yml b/.github/workflows/reset.yml new file mode 100644 index 0000000..1ecca20 --- /dev/null +++ b/.github/workflows/reset.yml @@ -0,0 +1,14 @@ +name: Reset cache + +on: + push: + branches: + - main + +jobs: + run-updater: + runs-on: ubuntu-latest + steps: + - name: Reset cache + run: | + curl -X GET "https://v3.avohq.io/templates/reset?secret=${{secrets.GH_API_SECRET}}&repo=$GITHUB_REPOSITORY" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a5e3888 --- /dev/null +++ b/readme.md @@ -0,0 +1,10 @@ +# Avo 3 to Jumpstart Pro template + +## Happening + +1. Add to the `Gemfile`. Possibly to show a choice for `avo`, `pro` or `advanced`. `gem "avo", ">= 3.0.1.beta4", source: "https://packager.dev/avo-hq"` +1. Run `bundle install` +1. Add the route `mount Avo::Engine, at: Avo.configuration.root_path` +1. Copy the `template` files + + diff --git a/template.rb b/template.rb new file mode 100644 index 0000000..2e1aed6 --- /dev/null +++ b/template.rb @@ -0,0 +1,59 @@ +# Insert templates here + +if ARGV.include? "--community-edition" + edition = "community" +elsif ARGV.include? "--pro-edition" + edition = "pro" +elsif ARGV.include? "--advanced-edition" + edition = "advanced" +end + +unless edition + # === Fetch the Avo edition === + question = <<~QUESTION + What version of Avo would you like to install? + 1. Avo Community (default) + 2. Avo Pro + 3. Avo Advanced + QUESTION + + answer = ask(question, default: "1", limited_to: ["1", "2", "3"]) + + edition = case answer + when "1" + "community" + when "2" + "pro" + when "3" + "advanced" + end +end + +# === Add gem to Gemfile === +case edition +when "community" + gem "avo", ">= 3.0.1.beta4", source: "https://packager.dev/avo-hq" +when "pro" + gem "avo-pro", source: "https://packager.dev/avo-hq" +when "advanced" + gem "avo-advanced", source: "https://packager.dev/avo-hq" +end + +# === Run bundle install === +Bundler.with_unbundled_env { run "bundle install" } + +# === Add route === +route_contents = <<-ROUTES + # Avo admin panel + if defined?(Avo::Engine) + authenticated :user, lambda { |u| u.admin? } do + mount Avo::Engine, at: Avo.configuration.root_path + end + end +ROUTES +route route_contents + +# === Copy template files === +files.each do |path, contents| + file path, contents +end diff --git a/template/app/avo/cards/active_subscriptions.rb b/template/app/avo/cards/active_subscriptions.rb new file mode 100644 index 0000000..865c9c5 --- /dev/null +++ b/template/app/avo/cards/active_subscriptions.rb @@ -0,0 +1,9 @@ +class Avo::Cards::ActiveSubscriptions < Avo::Dashboards::MetricCard + self.id = "active_subscriptions" + self.label = "Active subscriptions" + self.description = "Total number of active subscriptions" + + def query + result ::Pay::Subscription.active.count + end +end diff --git a/template/app/avo/cards/total_revenue.rb b/template/app/avo/cards/total_revenue.rb new file mode 100644 index 0000000..a89ae51 --- /dev/null +++ b/template/app/avo/cards/total_revenue.rb @@ -0,0 +1,44 @@ +class Avo::Cards::TotalRevenue < Avo::Dashboards::MetricCard + self.id = "total_revenue" + self.label = "Total revenue" + self.prefix = "$" + + def query + case arguments[:period] + when :last_12_months + result last_12_months + when :last_month + result last_month + when :this_month + result this_month + else + result total_revenue + end + end + + def total_revenue + revenue_in_cents = ::Pay::Charge.sum(:amount) + refunds_in_cents = ::Pay::Charge.sum(:amount_refunded) + (revenue_in_cents - refunds_in_cents) / 100.0 + end + + def last_12_months + revenue_for_range 12.months.ago..Time.current + end + + def last_month + month = Time.current.prev_month + revenue_for_range month.beginning_of_month..month.end_of_month + end + + def this_month + month = Time.current + revenue_for_range month.beginning_of_month..month.end_of_month + end + + def revenue_for_range(range) + revenue_in_cents = ::Pay::Charge.where(created_at: range).sum(:amount) + refunds_in_cents = ::Pay::Charge.where(created_at: range).sum(:amount_refunded) + (revenue_in_cents - refunds_in_cents) / 100.0 + end +end diff --git a/template/app/avo/cards/users_count.rb b/template/app/avo/cards/users_count.rb new file mode 100644 index 0000000..092c578 --- /dev/null +++ b/template/app/avo/cards/users_count.rb @@ -0,0 +1,9 @@ +class Avo::Cards::UsersCount < Avo::Dashboards::MetricCard + self.id = "users_count" + self.label = "Users count" + self.description = "Total number of users registered" + + def query + result ::User.all.count + end +end diff --git a/template/app/avo/dashboards/overview.rb b/template/app/avo/dashboards/overview.rb new file mode 100644 index 0000000..d1001cd --- /dev/null +++ b/template/app/avo/dashboards/overview.rb @@ -0,0 +1,30 @@ +class Avo::Dashboards::Overview < Avo::Dashboards::BaseDashboard + self.id = "overview" + self.name = "Overview" + self.grid_cols = 4 + + def cards + divider label: "Revenue" + card Avo::Cards::TotalRevenue, + arguments: { + period: :this_month + }, + label: "This month" + card Avo::Cards::TotalRevenue, + arguments: { + period: :last_month + }, + label: "Last month" + card Avo::Cards::TotalRevenue, + arguments: { + period: :last_12_months + }, + label: "Last 12 months" + card Avo::Cards::TotalRevenue + + divider + + card Avo::Cards::UsersCount + card Avo::Cards::ActiveSubscriptions + end +end diff --git a/template/app/avo/resources/invitation.rb b/template/app/avo/resources/invitation.rb new file mode 100644 index 0000000..c889121 --- /dev/null +++ b/template/app/avo/resources/invitation.rb @@ -0,0 +1,21 @@ +class InvitationResource < Avo::BaseResource + self.title = :email + self.includes = [] + # self.search = { + # query: -> { query.ransack(id_eq: params[:q], email_cont: params[:q], m: "or").result(distinct: false) }, + # item: -> { + # { + # title: record.title + # } + # } + # } + + def fields + field :id, as: :id + field :email, as: :text + field :uuid, as: :text + field :team, as: :belongs_to + field :from_membership, as: :belongs_to + field :membership, as: :has_one + end +end diff --git a/template/app/avo/resources/membership.rb b/template/app/avo/resources/membership.rb new file mode 100644 index 0000000..6636d83 --- /dev/null +++ b/template/app/avo/resources/membership.rb @@ -0,0 +1,23 @@ +class MembershipResource < Avo::BaseResource + self.title = :name + self.includes = [] + # self.search = { + # query: -> { query.ransack(id_eq: params[:q], name_cont: params[:q], user_email_cont: params[:q], role_cont: params[:q], m: "or").result(distinct: false) }, + # item: -> { + # { + # title: record.name + # } + # } + # } + + field :id, as: :id + field :name, as: :text, hide_on: :forms + field :user_profile_photo_id, as: :text + field :user_email, as: :text + field :added_by_id, as: :number + field :role_ids, as: :tags + field :user, as: :belongs_to + field :team, as: :belongs_to + field :invitation, as: :belongs_to + field :added_by, as: :belongs_to +end diff --git a/template/app/avo/resources/team.rb b/template/app/avo/resources/team.rb new file mode 100644 index 0000000..4f444d2 --- /dev/null +++ b/template/app/avo/resources/team.rb @@ -0,0 +1,24 @@ +class Avo::Resources::Team < Avo::BaseResource + self.title = :name + self.includes = [] + self.search = { + query: -> { query.ransack(id_eq: params[:q], name_cont: params[:q], slug_cont: params[:q], m: "or").result(distinct: false) }, + item: -> { + { + title: record.name + } + } + } + + def fields + field :id, as: :id + field :name, as: :text + field :slug, as: :text + field :being_destroyed, as: :boolean, hide_on: :forms + field :time_zone, as: :text + field :locale, as: :text + field :users, as: :has_many, through: :memberships + field :memberships, as: :has_many + field :invitations, as: :has_many + end +end diff --git a/template/app/avo/resources/user.rb b/template/app/avo/resources/user.rb new file mode 100644 index 0000000..393e5b1 --- /dev/null +++ b/template/app/avo/resources/user.rb @@ -0,0 +1,24 @@ +class UserResource < Avo::BaseResource + self.title = :full_name + self.includes = [:teams, :memberships] + self.search = { + query: -> { query.ransack(id_eq: params[:q], first_name_cont: params[:q], last_name_cont: params[:q], email_cont: params[:q], m: "or").result(distinct: false) }, + item: -> { + { + title: record.name, + } + } + } + + def fields + field :id, as: :id + field :email, as: :text + field :first_name, as: :text + field :last_name, as: :text + field :time_zone, as: :text + field :current_team, as: :belongs_to + + field :teams, as: :has_many, through: :teams + field :memberships, as: :has_many + end +end diff --git a/template/app/controllers/avo/invitations_controller.rb b/template/app/controllers/avo/invitations_controller.rb new file mode 100644 index 0000000..6f7ac10 --- /dev/null +++ b/template/app/controllers/avo/invitations_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/3.0/controllers.html +class Avo::InvitationsController < Avo::ResourcesController +end diff --git a/template/app/controllers/avo/memberships_controller.rb b/template/app/controllers/avo/memberships_controller.rb new file mode 100644 index 0000000..b92006c --- /dev/null +++ b/template/app/controllers/avo/memberships_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/3.0/controllers.html +class Avo::MembershipsController < Avo::ResourcesController +end diff --git a/template/app/controllers/avo/teams_controller.rb b/template/app/controllers/avo/teams_controller.rb new file mode 100644 index 0000000..76d7ecb --- /dev/null +++ b/template/app/controllers/avo/teams_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/3.0/controllers.html +class Avo::TeamsController < Avo::ResourcesController +end diff --git a/template/app/controllers/avo/users_controller.rb b/template/app/controllers/avo/users_controller.rb new file mode 100644 index 0000000..a9987c6 --- /dev/null +++ b/template/app/controllers/avo/users_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/3.0/controllers.html +class Avo::UsersController < Avo::ResourcesController +end diff --git a/template/config/initializers/avo.rb b/template/config/initializers/avo.rb new file mode 100644 index 0000000..a153c70 --- /dev/null +++ b/template/config/initializers/avo.rb @@ -0,0 +1,122 @@ +# For more information regarding these settings check out our docs https://docs.avohq.io +Avo.configure do |config| + ## == Routing == + config.root_path = "/avo" + # used only when you have custom `map` configuration in your config.ru + # config.prefix_path = "/internal" + + # Where should the user be redirected when visting the `/avo` url + config.home_path = "/dashboards/overview" + + ## == Licensing == + # Add your license key here for Pro or Advanced licenses + # config.license_key = ENV['AVO_LICENSE_KEY'] + + ## == Set the context == + config.set_context do + # Return a context object that gets evaluated in Avo::ApplicationController + {} + end + + ## == Authentication == + # config.current_user_method = {} + # config.authenticate_with do + # end + + ## == Authorization == + # config.authorization_methods = { + # index: 'index?', + # show: 'show?', + # edit: 'edit?', + # new: 'new?', + # update: 'update?', + # create: 'create?', + # destroy: 'destroy?', + # search: 'search?', + # } + # config.raise_error_on_missing_policy = false + # config.authorization_client = :pundit + + ## == Localization == + # config.locale = 'en-US' + + ## == Resource options == + # config.resource_controls_placement = :right + # config.model_resource_mapping = {} + # config.default_view_type = :table + # config.per_page = 24 + # config.per_page_steps = [12, 24, 48, 72] + # config.via_per_page = 8 + config.id_links_to_resource = true + # config.cache_resources_on_index_view = true + ## permanent enable or disable cache_resource_filters, default value is false + # config.cache_resource_filters = false + ## provide a lambda to enable or disable cache_resource_filters per user/resource. + # config.cache_resource_filters = ->(current_user:, resource:) { current_user.cache_resource_filters?} + + ## == Customization == + # config.app_name = 'Avocadelicious' + # config.timezone = 'UTC' + # config.currency = 'USD' + # config.hide_layout_when_printing = false + # config.full_width_container = false + # config.full_width_index_view = false + # config.search_debounce = 300 + # config.view_component_path = "app/components" + # config.display_license_request_timeout_error = true + # config.disabled_features = [] + # config.tabs_style = :tabs # can be :tabs or :pills + # config.buttons_on_form_footers = true + # config.field_wrapper_layout = true + + ## == Branding == + # config.branding = { + # colors: { + # background: "248 246 242", + # 100 => "#CEE7F8", + # 400 => "#399EE5", + # 500 => "#0886DE", + # 600 => "#066BB2", + # }, + # chart_colors: ["#0B8AE2", "#34C683", "#2AB1EE", "#34C6A8"], + # logo: "/avo-assets/logo.png", + # logomark: "/avo-assets/logomark.png", + # placeholder: "/avo-assets/placeholder.svg", + # favicon: "/avo-assets/favicon.ico" + # } + + ## == Breadcrumbs == + # config.display_breadcrumbs = true + # config.set_initial_breadcrumbs do + # add_breadcrumb "Home", '/avo' + # end + + ## == Menus == + config.main_menu = -> { + section "Dashboards", icon: "dashboards" do + all_dashboards + end + + section "Users", icon: "heroicons/outline/user-group" do + resource :announcement + resource :user + resource :account + resource :account_user + resource :plan + end + + section "Pay", icon: "heroicons/outline/currency-dollar" do + resource :customer + resource :charge + resource :payment_method + resource :subscription + end + + if Rails.env.development? + link "Jumpstart Config", path: Avo::Current.view_context.main_app.jumpstart_path + end + } + config.profile_menu = -> { + link "Profile", path: "/avo/profile", icon: "user-circle" + } +end