From 4f877ab05d18759ed9129d5b94347e68d506de8e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Thu, 29 Oct 2020 15:53:29 +0100 Subject: [PATCH] react coponent: plan-card + extracted scss from stylesheet.rb into themes/ --- Gemfile | 3 + Gemfile.lock | 3 + .../src/javascript/components/plan-card.tsx | 56 +++- .../src/javascript/controllers/plans.js | 27 +- app/frontend/src/javascript/models/user.ts | 85 +++++ .../src/stylesheets/app.components.scss | 5 +- app/frontend/templates/plans/index.html | 25 +- app/models/stylesheet.rb | 60 +--- app/themes/casemate/style.scss.erb | 295 ++++++++++++++++++ app/views/api/members/_member.json.jbuilder | 3 +- app/views/api/members/show.json.jbuilder | 2 +- 11 files changed, 477 insertions(+), 87 deletions(-) create mode 100644 app/frontend/src/javascript/models/user.ts create mode 100644 app/themes/casemate/style.scss.erb diff --git a/Gemfile b/Gemfile index 17514aa62..7a07ab9e9 100644 --- a/Gemfile +++ b/Gemfile @@ -136,3 +136,6 @@ gem 'repost' gem 'icalendar' gem 'tzinfo-data' + +# compilation of dynamic stylesheets (home page & theme) +gem 'sassc' diff --git a/Gemfile.lock b/Gemfile.lock index 564cdf697..90c2e83b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -336,6 +336,8 @@ GEM rubyzip (>= 1.3.0) rubyzip (1.3.0) safe_yaml (1.0.5) + sassc (2.2.1) + ffi (~> 1.9) seed_dump (3.3.1) activerecord (>= 4) activesupport (>= 4) @@ -476,6 +478,7 @@ DEPENDENCIES rubocop (~> 0.61.1) rubyXL rubyzip (>= 1.3.0) + sassc seed_dump sha3 sidekiq (>= 6.0.7) diff --git a/app/frontend/src/javascript/components/plan-card.tsx b/app/frontend/src/javascript/components/plan-card.tsx index a5597fcb1..04c8cf5f3 100644 --- a/app/frontend/src/javascript/components/plan-card.tsx +++ b/app/frontend/src/javascript/components/plan-card.tsx @@ -5,19 +5,25 @@ import React from 'react'; import { react2angular } from 'react2angular'; import { IFilterService } from 'angular'; -import moment from "moment"; +import moment from 'moment'; +import _ from 'lodash' import { IApplication } from '../models/application'; import { Plan } from '../models/plan'; +import { User, UserRole } from '../models/user'; declare var Application: IApplication; interface PlanCardProps { plan: Plan, - _t: (key: string, interpolation: object) => string, + user: User, + operator: User, + isSelected: boolean, + onSelectPlan: (plan: Plan) => void, + _t: (key: string, interpolation?: object) => Promise, $filter: IFilterService } -const PlanCard: React.FC = ({ plan, _t, $filter }) => { +const PlanCard: React.FC = ({ plan, user, operator, onSelectPlan, isSelected, _t, $filter }) => { /** * Return the formatted localized amount of the given plan (eg. 20.5 => "20,50 €") */ @@ -30,6 +36,30 @@ const PlanCard: React.FC = ({ plan, _t, $filter }) => { const duration = (): string => { return moment.duration(plan.interval_count, plan.interval).humanize(); } + /** + * Check if the user can subscribe to the current plan, for himself + */ + const canSubscribeForMe = (): boolean => { + return operator?.role === UserRole.Member || (operator?.role === UserRole.Manager && user?.id === operator?.id) + } + /** + * Check if the user can subscribe to the current plan, for someone else + */ + const canSubscribeForOther = (): boolean => { + return operator?.role === UserRole.Admin || (operator?.role === UserRole.Manager && user?.id !== operator?.id) + } + /** + * Check it the user has subscribed to this plan or not + */ + const hasSubscribedToThisPlan = (): boolean => { + return user?.subscription?.plan?.id === plan.id; + } + /** + * Callback triggered when the user select the plan + */ + const handleSelectPlan = (): void => { + onSelectPlan(plan); + } return (

{plan.base_name}

@@ -41,8 +71,26 @@ const PlanCard: React.FC = ({ plan, _t, $filter }) => {
+ {canSubscribeForMe() &&
+ {!hasSubscribedToThisPlan() && } + {hasSubscribedToThisPlan() && } +
} + {canSubscribeForOther() &&
+ +
} ); } -Application.Components.component('planCard', react2angular(PlanCard, ['plan'], ['_t', '$filter'])); +Application.Components.component('planCard', react2angular(PlanCard, ['plan', 'user', 'operator', 'onSelectPlan', 'isSelected'], ['_t', '$filter'])); diff --git a/app/frontend/src/javascript/controllers/plans.js b/app/frontend/src/javascript/controllers/plans.js index 7d4e0d32c..c4af2a91b 100644 --- a/app/frontend/src/javascript/controllers/plans.js +++ b/app/frontend/src/javascript/controllers/plans.js @@ -72,16 +72,27 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop * @param plan {Object} The plan to subscribe to */ $scope.selectPlan = function (plan) { - if ($scope.isAuthenticated()) { - if ($scope.selectedPlan !== plan) { - $scope.selectedPlan = plan; - updateCartPrice(); + setTimeout(() => { + if ($scope.isAuthenticated()) { + if ($scope.selectedPlan !== plan) { + $scope.selectedPlan = plan; + updateCartPrice(); + } else { + $scope.selectedPlan = null; + } } else { - $scope.selectedPlan = null; + $scope.login(); } - } else { - $scope.login(); - } + $scope.$apply(); + }, 50); + }; + + /** + * Check if the provided plan is currently selected + * @param plan {Object} Resource plan + */ + $scope.isSelected = function (plan) { + return $scope.selectedPlan === plan; }; /** diff --git a/app/frontend/src/javascript/models/user.ts b/app/frontend/src/javascript/models/user.ts new file mode 100644 index 000000000..ebd9710c8 --- /dev/null +++ b/app/frontend/src/javascript/models/user.ts @@ -0,0 +1,85 @@ +import { Plan } from './plan'; + + +export enum UserRole { + Member = 'member', + Manager = 'manager', + Admin = 'admin' +} + +export interface User { + id: number, + username: string, + email: string, + group_id: number, + role: UserRole + name: string, + need_completion: boolean, + profile: { + id: number, + first_name: string, + last_name: string, + interest: string, + software_mastered: string, + phone: string, + website: string, + job: string, + tours: Array, + facebook: string, + twitter: string, + google_plus: string, + viadeo: string, + linkedin: string, + instagram: string, + youtube: string, + vimeo: string, + dailymotion: string, + github: string, + echosciences: string, + pinterest: string, + lastfm: string, + flickr: string, + user_avatar: { + id: number, + attachment_url: string + } + }, + invoicing_profile: { + id: number, + address: { + id: number, + address: string + }, + organization: { + id: number, + name: string, + address: { + id: number, + address: string + } + } + }, + statistic_profile: { + id: number, + gender: string, + birthday: Date + }, + subscribed_plan: Plan, + subscription: { + id: number, + expired_at: Date, + canceled_at: Date, + stripe: boolean, + plan: { + id: number, + base_name: string, + name: string, + interval: string, + interval_count: number, + amount: number + } + }, + training_credits: Array, + machine_credits: Array<{machine_id: number, hours_used: number}>, + last_sign_in_at: Date +} diff --git a/app/frontend/src/stylesheets/app.components.scss b/app/frontend/src/stylesheets/app.components.scss index 7c443bde1..b3c51a807 100644 --- a/app/frontend/src/stylesheets/app.components.scss +++ b/app/frontend/src/stylesheets/app.components.scss @@ -333,7 +333,10 @@ .cta-button { margin: 20px 0; - .btn { + .subscribe-button { + @extend .btn; + @extend .rounded; + outline: 0; font-weight: 600; font-size: rem-calc(16); diff --git a/app/frontend/templates/plans/index.html b/app/frontend/templates/plans/index.html index 7d2349301..e9642bf67 100644 --- a/app/frontend/templates/plans/index.html +++ b/app/frontend/templates/plans/index.html @@ -31,25 +31,12 @@ ng-class="{'col-md-12 col-lg-12 b-r':(plansGroup.plans.filter(filterDisabledPlans).length % 2 == 1 && key == plansGroup.plans.filter(filterDisabledPlans).length-1)}" ng-repeat="(key, plan) in plansGroup.plans.filter(filterDisabledPlans) | orderBy: '-ui_weight'"> - - -
- - - - -
- -
- -
+ +
{{ 'app.public.plans.more_information' }} diff --git a/app/models/stylesheet.rb b/app/models/stylesheet.rb index 436393e2d..dd0b51671 100644 --- a/app/models/stylesheet.rb +++ b/app/models/stylesheet.rb @@ -15,7 +15,7 @@ class Stylesheet < ApplicationRecord def rebuild! if Stylesheet.primary && Stylesheet.secondary && name == 'theme' - update(contents: Stylesheet.css) + update(contents: Stylesheet.theme_css) elsif name == 'home_page' update(contents: Stylesheet.home_page_css) end @@ -29,7 +29,7 @@ class Stylesheet < ApplicationRecord if Stylesheet.theme Stylesheet.theme.rebuild! else - Stylesheet.create!(contents: Stylesheet.css, name: 'theme') + Stylesheet.create!(contents: Stylesheet.theme_css, name: 'theme') end end @@ -77,56 +77,10 @@ class Stylesheet < ApplicationRecord Stylesheet.secondary.paint.brightness <= BRIGHTNESS_LOW_LIMIT ? 'white' : 'black' end - def self.css # rubocop:disable Metrics/AbcSize - <<~CSS - .bg-red { background-color: #{Stylesheet.primary}; } - .bg-red-dark { background-color: #{Stylesheet.primary}; } - #nav .nav { background-color: #{Stylesheet.primary}; } - #nav .nav > li > a { color: #{Stylesheet.primary_text_color}; } - #nav .nav > li > a:hover, #nav .nav > li > a:focus { background-color: #{Stylesheet.primary_light}; color: #{Stylesheet.primary_text_color}; } - #nav .nav > li > a.active { border-left: 3px solid #{Stylesheet.primary_dark}; background-color: #{Stylesheet.primary_light}; color: #{Stylesheet.primary_text_color}; } - .nav-primary ul.nav > li.menu-spacer { background: linear-gradient(45deg, #{Stylesheet.primary_decoration_color}, transparent); } - .nav-primary .text-bordeau { color: #{Stylesheet.primary_decoration_color}; } - .btn-theme { background-color: #{Stylesheet.primary}; color: #{Stylesheet.primary_text_color}; } - .btn-theme:active, .btn-theme:hover { background-color: #{Stylesheet.primary_dark}; color: #{Stylesheet.primary_text_color}; } - .label-theme { background-color: #{Stylesheet.primary}; color: #{Stylesheet.primary_text_color}; } - .btn-link { color: #{Stylesheet.primary} !important; } - .btn-link:hover { color: #{Stylesheet.primary_dark} !important; } - a { color: #{Stylesheet.primary}; } - .about-page-link a.about-link, .user-profile-nav b.caret { color: #{Stylesheet.primary}; } - .app-generator a, .home-events h4 a, a.reinit-filters, .pricing-panel a, .calendar-url a, .article a, a.project-author, a.dsq-brlink, .alert a, .about-fablab a, a.collected-infos { color: #{Stylesheet.primary}; } - .app-generator a:hover, .home-events h4 a:hover, a.reinit-filters:hover, .pricing-panel a:hover, .calendar-url a:hover, .article a:hover, a.project-author:hover, a.dsq-brlink:hover, .widget .widget-content a:hover, .alert a:hover, .about-fablab a:hover, a.collected-infos:hover { color: #{Stylesheet.primary_dark}; } - .btn.btn-default.reserve-button, .btn.btn-default.show-button, .btn.btn-default.red { color: #{Stylesheet.primary} !important; } - .nav.nav-tabs .uib-tab.nav-item:not(.active) a { color: #{Stylesheet.primary}; } - .article h2, .article h3, .article h5 { color: #{Stylesheet.primary} !important; } - table.table thead tr th a, table.table tbody tr td a:not(.btn) { color: #{Stylesheet.primary}; } - table.table thead tr th a:hover, table.table tbody tr td a:not(.btn):hover { color: #{Stylesheet.primary_dark}; } - a:hover, a:focus { color: #{Stylesheet.primary_dark}; } - h2, h3, h3.red, h5 { color: #{Stylesheet.primary} !important; } - h5:after { background-color: #{Stylesheet.primary}; } - .bg-yellow { background-color: #{Stylesheet.secondary} !important; color: #{Stylesheet.secondary_text_color}; } - .event:hover { background-color: #{Stylesheet.primary}; color: #{Stylesheet.secondary_text_color}; } - .widget h3 { color: #{Stylesheet.primary}; } - .modal-header h1, .custom-invoice .modal-header h1 { color: #{Stylesheet.primary}; } - .block-link:hover, .fc-toolbar .fc-button:hover, .fc-toolbar .fc-button:active, .fc-toolbar .fc-button.fc-state-active { background-color: #{Stylesheet.secondary}; color: #{Stylesheet.secondary_text_color} !important; } - .block-link:hover .user-name { color: #{Stylesheet.secondary_text_color} !important; } - .carousel-control:hover, .carousel-control:focus, .carousel-caption .title a:hover { color: #{Stylesheet.secondary}; } - .well.well-warning { border-color: #{Stylesheet.secondary}; background-color: #{Stylesheet.secondary}; color: #{Stylesheet.secondary_text_color}; } - .text-yellow { color: #{Stylesheet.secondary} !important; } - .red { color: #{Stylesheet.primary} !important; } - .btn-warning, .editable-buttons button[type=submit].btn-primary { background-color: #{Stylesheet.secondary} !important; border-color: #{Stylesheet.secondary} !important; color: #{Stylesheet.secondary_text_color}; } - .btn-warning:hover, .editable-buttons button[type=submit].btn-primary:hover, .btn-warning:focus, .editable-buttons button[type=submit].btn-primary:focus, .btn-warning.focus, .editable-buttons button.focus[type=submit].btn-primary, .btn-warning:active, .editable-buttons button[type=submit].btn-primary:active, .btn-warning.active, .editable-buttons button.active[type=submit].btn-primary, .open > .btn-warning.dropdown-toggle, .editable-buttons .open > button.dropdown-toggle[type=submit].btn-primary { background-color: #{Stylesheet.secondary_dark} !important; border-color: #{Stylesheet.secondary_dark} !important; color: #{Stylesheet.secondary_text_color}; } - .btn-warning-full { border-color: #{Stylesheet.secondary}; background-color: #{Stylesheet.secondary}; color: #{Stylesheet.secondary_text_color} !important; } - .heading .heading-btn a:hover { background-color: #{Stylesheet.secondary}; color: #{Stylesheet.secondary_text_color}; } - .pricing-panel .content .wrap { border-color: #{Stylesheet.secondary}; } - .pricing-panel .cta-button .btn:hover, .pricing-panel .cta-button .custom-invoice .modal-body .elements li:hover, .custom-invoice .modal-body .elements .pricing-panel .cta-button li:hover { background-color: #{Stylesheet.secondary} !important; color: #{Stylesheet.secondary_text_color}; } - a.label:hover, .form-control.form-control-ui-select .select2-choices a.select2-search-choice:hover, a.label:focus, .form-control.form-control-ui-select .select2-choices a.select2-search-choice:focus { color: #{Stylesheet.primary}; } - .about-picture { background: linear-gradient( rgba(255,255,255,0.12), rgba(255,255,255,0.13) ), linear-gradient( #{Stylesheet.primary_with_alpha(0.78)}, #{Stylesheet.primary_with_alpha(0.82)} ), url('/about-fablab.jpg') no-repeat; } - .social-icons > div:hover { background-color: #{Stylesheet.secondary}; color: #{Stylesheet.secondary_text_color}; } - .profile-top { background: linear-gradient( rgba(255,255,255,0.12), rgba(255,255,255,0.13) ), linear-gradient(#{Stylesheet.primary_with_alpha(0.78)}, #{Stylesheet.primary_with_alpha(0.82)} ), url('#{CustomAsset.get_url('profile-image-file') || '/about-fablab.jpg'}') no-repeat; } - .profile-top .social-links a:hover { background-color: #{Stylesheet.secondary} !important; border-color: #{Stylesheet.secondary} !important; color: #{Stylesheet.secondary_text_color}; } - section#cookies-modal div.cookies-consent .cookies-actions button.accept { background-color: #{Stylesheet.secondary}; color: #{Stylesheet.secondary_text_color}; } - CSS + def self.theme_css + template = ERB.new(File.read('app/themes/casemate/style.scss.erb')).result + engine = SassC::Engine.new(template, style: :compressed) + engine.render.presence end ## ===== HOME PAGE ===== @@ -149,7 +103,7 @@ class Stylesheet < ApplicationRecord end def self.home_page_css - engine = Sass::Engine.new(home_style, syntax: :scss) + engine = SassC::Engine.new(home_style, style: :compressed) engine.render.presence || '.home-page {}' end end diff --git a/app/themes/casemate/style.scss.erb b/app/themes/casemate/style.scss.erb new file mode 100644 index 000000000..256549446 --- /dev/null +++ b/app/themes/casemate/style.scss.erb @@ -0,0 +1,295 @@ +$primary: <%= Stylesheet.primary %> !default; +$primary-light: <%= Stylesheet.primary_light %> !default; +$primary-dark: <%= Stylesheet.primary_dark %> !default; + +$secondary: <%= Stylesheet.secondary %> !default; +$secondary-light: <%= Stylesheet.secondary_light %> !default; +$secondary-dark: <%= Stylesheet.secondary_dark %> !default; + +$primary-text-color: <%= Stylesheet.primary_text_color %> !default; +$secondary-text-color: <%= Stylesheet.secondary_text_color %> !default; + +$primary-decoration-color: <%= Stylesheet.primary_decoration_color %> !default; + +.bg-red { + background-color: $primary; +} + +.bg-red-dark { + background-color: $primary; +} + +#nav .nav { + background-color: $primary; +} + +#nav .nav > li > a { + color: $primary-text-color; +} + +#nav .nav > li > a:hover, +#nav .nav > li > a:focus { + background-color: $primary-light; + color: $primary-text-color; +} + +#nav .nav > li > a.active { + border-left: 3px solid $primary-dark; + background-color: $primary-light; + color: $primary-text-color; +} + +.nav-primary ul.nav > li.menu-spacer { + background: linear-gradient(45deg, $primary-decoration-color, transparent); +} + +.nav-primary .text-bordeau { + color: $primary-decoration-color; +} + +.btn-theme { + background-color: $primary; + color: $primary-text-color; +} + +.btn-theme:active, +.btn-theme:hover { + background-color: $primary-dark; + color: $primary-text-color; +} + +.label-theme { + background-color: $primary; + color: $primary-text-color; +} + +.btn-link { + color: $primary !important; +} + +.btn-link:hover { + color: $primary-dark !important; +} + +a { + color: $primary; +} + +.about-page-link a.about-link, +.user-profile-nav b.caret { + color: $primary; +} + +.app-generator a, +.home-events h4 a, +a.reinit-filters, +.pricing-panel a, +.calendar-url a, +.article a, +a.project-author, +a.dsq-brlink, +.alert a, +.about-fablab a, +a.collected-infos { + color: $primary; +} + +.app-generator a:hover, +.home-events h4 a:hover, +a.reinit-filters:hover, +.pricing-panel a:hover, +.calendar-url a:hover, +.article a:hover, +a.project-author:hover, +a.dsq-brlink:hover, +.widget .widget-content a:hover, +.alert a:hover, +.about-fablab a:hover, +a.collected-infos:hover { + color: $primary-dark; +} + +.btn.btn-default.reserve-button, +.btn.btn-default.show-button, +.btn.btn-default.red { + color: $primary !important; +} + +.nav.nav-tabs .uib-tab.nav-item:not(.active) a { + color: $primary; +} + +.article h2, +.article h3, +.article h5 { + color: $primary !important; +} + +table.table thead tr th a, +table.table tbody tr td a:not(.btn) { + color: $primary; +} + +table.table thead tr th a:hover, +table.table tbody tr td a:not(.btn):hover { + color: $primary-dark; +} + +a:hover, +a:focus { + color: $primary-dark; +} + +h2, +h3, +h3.red, +h5 { + color: $primary !important; +} + +h5:after { + background-color: $primary; +} + +.bg-yellow { + background-color: $secondary !important; + color: $secondary-text-color; +} + +.event:hover { + background-color: $primary; + color: $secondary-text-color; +} + +.widget h3 { + color: $primary; +} + +.modal-header h1, +.custom-invoice .modal-header h1 { + color: $primary; +} + +.block-link:hover, +.fc-toolbar .fc-button:hover, +.fc-toolbar .fc-button:active, +.fc-toolbar .fc-button.fc-state-active { + background-color: $secondary; + color: $secondary-text-color !important; +} + +.block-link:hover .user-name { + color: $secondary-text-color !important; +} + +.carousel-control:hover, +.carousel-control:focus, +.carousel-caption .title a:hover { + color: $secondary; +} + +.well.well-warning { + border-color: $secondary; + background-color: $secondary; + color: $secondary-text-color; +} + +.text-yellow { + color: $secondary !important; +} + +.red { + color: $primary !important; +} + +.btn-warning, +.editable-buttons button[type=submit].btn-primary { + background-color: $secondary !important; + border-color: $secondary !important; + color: $secondary-text-color; +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning.focus, +.btn-warning:active, +.btn-warning.active, +.editable-buttons button[type=submit].btn-primary:hover, +.editable-buttons button[type=submit].btn-primary:focus, +.editable-buttons button.focus[type=submit].btn-primary, +.editable-buttons button[type=submit].btn-primary:active, +.editable-buttons button.active[type=submit].btn-primary, +.open > .btn-warning.dropdown-toggle, +.editable-buttons .open > button.dropdown-toggle[type=submit].btn-primary { + background-color: $secondary-dark !important; + border-color: $secondary-dark !important; + color: $secondary-text-color; +} + +.btn-warning-full { + border-color: $secondary; + background-color: $secondary; + color: $secondary-text-color !important; +} + +.heading .heading-btn a:hover { + background-color: $secondary; + color: $secondary-text-color; +} + +.pricing-panel .content .wrap { + border-color: $secondary; +} + +.pricing-panel .cta-button .btn:hover, +.pricing-panel .cta-button .custom-invoice .modal-body .elements li:hover, +.custom-invoice .modal-body .elements .pricing-panel .cta-button li:hover { + background-color: $secondary !important; + color: $secondary-text-color; +} + +a.label:hover, +a.label:focus, +.form-control.form-control-ui-select .select2-choices a.select2-search-choice:hover, +.form-control.form-control-ui-select .select2-choices a.select2-search-choice:focus { + color: $primary; +} + +.about-picture { + background: linear-gradient( rgba(255,255,255,0.12), rgba(255,255,255,0.13) ), + linear-gradient(<%=Stylesheet.primary_with_alpha(0.78)%>, <%=Stylesheet.primary_with_alpha(0.82)%>), + url('/about-fablab.jpg') no-repeat; +} + +.social-icons > div:hover { + background-color: $secondary; + color: $secondary-text-color; +} + +.profile-top { + background: linear-gradient( rgba(255,255,255,0.12), rgba(255,255,255,0.13) ), + linear-gradient(<%=Stylesheet.primary_with_alpha(0.78)%>, <%=Stylesheet.primary_with_alpha(0.82)%>), + url("<%=CustomAsset.get_url('profile-image-file') || '/about-fablab.jpg'%>") no-repeat; +} + +.profile-top .social-links a:hover { + background-color: $secondary !important; + border-color: $secondary !important; + color: $secondary-text-color; +} + +section#cookies-modal div.cookies-consent .cookies-actions button.accept { + background-color: $secondary; + color: $secondary-text-color; +} + +.pricing-panel { + .cta-button { + button.subscribe-button { + border-color: $secondary; + &.selected-card { + background-color: $secondary; + } + } + } +} diff --git a/app/views/api/members/_member.json.jbuilder b/app/views/api/members/_member.json.jbuilder index 84d4d4380..637bb711a 100644 --- a/app/views/api/members/_member.json.jbuilder +++ b/app/views/api/members/_member.json.jbuilder @@ -65,7 +65,7 @@ if member.subscription json.expired_at member.subscription.expired_at.iso8601 json.canceled_at member.subscription.canceled_at.iso8601 if member.subscription.canceled_at json.stripe member.subscription.stp_subscription_id.present? - json.plan do + json.plan do # TODO, refactor: duplicates subscribed_plan json.id member.subscription.plan.id json.base_name member.subscription.plan.base_name json.name member.subscription.plan.name @@ -82,4 +82,5 @@ json.machine_credits member.machine_credits do |mc| json.machine_id mc.creditable_id json.hours_used mc.users_credits.find_by(user_id: member.id).hours_used end +# TODO, missing space_credits? json.last_sign_in_at member.last_sign_in_at.iso8601 if member.last_sign_in_at diff --git a/app/views/api/members/show.json.jbuilder b/app/views/api/members/show.json.jbuilder index edcc2e53c..42a9d9d4a 100644 --- a/app/views/api/members/show.json.jbuilder +++ b/app/views/api/members/show.json.jbuilder @@ -40,7 +40,7 @@ json.all_projects @member.all_projects do |project| if requested_current || project.state == 'published' json.extract! project, :id, :name, :description, :licence_id, :slug, :state json.author_id project.author.user_id - + json.project_image project.project_image.attachment.large.url if project.project_image json.machine_ids project.machine_ids json.machines project.machines do |m|