Skip to content

Commit

Permalink
Merge pull request #143 from debtcollective/feat/split-user-back-office
Browse files Browse the repository at this point in the history
Feat/split user back office
  • Loading branch information
castrolem authored Nov 22, 2019
2 parents 39bb224 + cd9a009 commit 5a67fab
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 41 deletions.
26 changes: 23 additions & 3 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
# frozen_string_literal: true

class UsersController < ApplicationController
before_action :set_user, only: %i[show]
layout 'backoffice'
before_action :set_user
before_action :verify_current_user

# GET /users/1
# GET /users/1.json
def show
redirect_to root_path unless current_user == @user
current_page_title('Your dashboard')
end

# pages
def subscription
current_page_title('Subscription management')
@is_subscription_changing = UserPlanChange.where(user_id: @user.id, status: 'pending').first
end

def donation_history
current_page_title('Donation history')
end

private

def current_page_title(page_title)
@current_page_title ||= page_title
end

def verify_current_user
redirect_to root_path unless current_user == @user
end

def set_user
@user = User.find(params[:id])
user_param_id = params[:id] || params[:user_id]
@user = User.find(user_param_id)
end
end
159 changes: 159 additions & 0 deletions app/javascript/bundles/User/components/Drawer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React from 'react'
import PropTypes from 'prop-types'
import AppBar from '@material-ui/core/AppBar'
import CssBaseline from '@material-ui/core/CssBaseline'
import Divider from '@material-ui/core/Divider'
import Drawer from '@material-ui/core/Drawer'
import Hidden from '@material-ui/core/Hidden'
import IconButton from '@material-ui/core/IconButton'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
import UsersIcon from '@material-ui/icons/SupervisedUserCircle'
import BookIcon from '@material-ui/icons/Book'
import SubscriptionsIcon from '@material-ui/icons/Subscriptions'
import MenuIcon from '@material-ui/icons/Menu'
import Toolbar from '@material-ui/core/Toolbar'
import Typography from '@material-ui/core/Typography'
import { makeStyles, useTheme } from '@material-ui/core/styles'

const drawerWidth = 240

const useStyles = makeStyles(theme => ({
root: {
display: 'flex'
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0
}
},
appBar: {
marginLeft: drawerWidth,
[theme.breakpoints.up('sm')]: {
width: `calc(100% - ${drawerWidth}px)`
}
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none'
}
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth
},
content: {
flexGrow: 1,
padding: theme.spacing(3)
}
}))

function ResponsiveDrawer (props) {
const { container } = props
const classes = useStyles()
const theme = useTheme()
const [mobileOpen, setMobileOpen] = React.useState(false)

function handleDrawerToggle () {
setMobileOpen(!mobileOpen)
}

const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>
<ListItem button component='a' href={`/users/${props.user.id}`}>
<ListItemIcon>
<UsersIcon />
</ListItemIcon>
<ListItemText primary='Dashboard' />
</ListItem>
<ListItem
button
component='a'
href={`/users/${props.user.id}/subscription`}
>
<ListItemIcon>
<SubscriptionsIcon />
</ListItemIcon>
<ListItemText primary='Subscription' />
</ListItem>
<ListItem
button
component='a'
href={`/users/${props.user.id}/donations`}
>
<ListItemIcon>
<BookIcon />
</ListItemIcon>
<ListItemText primary='Donations' />
</ListItem>
</List>
</div>
)

return (
<div className={classes.root}>
<CssBaseline />
<AppBar position='fixed' className={classes.appBar}>
<Toolbar>
<IconButton
color='inherit'
aria-label='open drawer'
edge='start'
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<Typography variant='h6' noWrap>
{props.pageTitle}
</Typography>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label='mailbox folders'>
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation='css'>
<Drawer
container={container}
variant='temporary'
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation='css'>
<Drawer
classes={{
paper: classes.drawerPaper
}}
variant='permanent'
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
</div>
)
}

ResponsiveDrawer.propTypes = {
pageTitle: PropTypes.string.isRequired,
user: PropTypes.object.isRequired
}

export default props => <ResponsiveDrawer {...props} />
4 changes: 3 additions & 1 deletion app/javascript/packs/user-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { UserShow } from '../bundles/User/components/UserShow'
import { DonationsHistory } from '../bundles/User/components/DonationsHistory'
import { SubscriptionCancel } from '../bundles/User/components/SubscriptionCancel'
import { CurrentPlan } from '../bundles/User/components/CurrentPlan'
import Drawer from '../bundles/User/components/Drawer'

ReactOnRails.register({
UserShow,
DonationsHistory,
SubscriptionCancel,
CurrentPlan
CurrentPlan,
Drawer
})
35 changes: 35 additions & 0 deletions app/views/layouts/backoffice.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<title>Fundraising</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= content_for?(:head) ? yield(:head) : '' %>

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'https://checkout.stripe.com/checkout.js' %>
<%= javascript_include_tag 'https://js.stripe.com/v3/' %>
<%= javascript_pack_tag 'user-bundle' %>
<link href="https://fonts.googleapis.com/css?family=Libre+Franklin&display=swap" rel="stylesheet">

<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
</head>

<body id="admin-views">
<%= react_component("Drawer", props: {pageTitle: @current_page_title, user: @user}) %>
<main>
<div class="content">
<%= yield %>
</div>
</main>
</body>
</html>
5 changes: 5 additions & 0 deletions app/views/users/donation_history.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<section class="section-content">
<p id="notice"><%= notice %></p>

<%= react_component("DonationsHistory", props: {donations: @user.donations}) %>
</section>
2 changes: 0 additions & 2 deletions app/views/users/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@
<p id="notice"><%= notice %></p>

<%= react_component("UserShow", props: {user: @user, streak: @user.current_streak, subscription: @user.active_subscription}) %>
<%= react_component("SubscriptionCancel", props: {user: @user, subscription: @user.active_subscription, activePlan: @user.active_subscription&.plan, isSubscriptionChanging: @is_subscription_changing}) %>
<%= react_component("DonationsHistory", props: {donations: @user.donations}) %>
</section>
5 changes: 5 additions & 0 deletions app/views/users/subscription.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<section class="section-content">
<p id="notice"><%= notice %></p>

<%= react_component("SubscriptionCancel", props: {user: @user, subscription: @user.active_subscription, activePlan: @user.active_subscription&.plan, isSubscriptionChanging: @is_subscription_changing}) %>
</section>
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
end

resources :users, only: %i[show new edit update create destroy] do
get '/subscription' => 'users#subscription', as: :current_subscription
get '/donations' => 'users#donation_history', as: :latest_donations
resource :streak, only: %i[show]
resources :cards, only: %i[index destroy]
resource :subscription, only: %i[destroy]
Expand Down
3 changes: 3 additions & 0 deletions spec/features/plan_change_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
within '#contact-data' do
expect(page).to have_content(user.name)
end

visit user_current_subscription_path(user)

expect(page).to have_content("You're subscribed")

click_link 'Change Subscription'
Expand Down
64 changes: 29 additions & 35 deletions spec/features/user/users_profile_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,43 @@
require 'rails_helper'

describe 'User - manages their profile', type: :feature, js: true do
describe 'home' do
let!(:user) { FactoryBot.create(:user) }
let!(:subscription) { FactoryBot.create(:subscription, user_id: user.id, active: true) }
let!(:one_time_donations) { FactoryBot.create_list(:donation, 5, user_id: user.id, donation_type: Donation::DONATION_TYPES[:one_off]) }
let!(:user) { FactoryBot.create(:user) }
let!(:subscription) { FactoryBot.create(:subscription, user_id: user.id, active: true) }
let!(:one_time_donations) { FactoryBot.create_list(:donation, 5, user_id: user.id, donation_type: Donation::DONATION_TYPES[:one_off]) }

before(:each) do
allow_any_instance_of(SessionProvider).to receive(:current_user).and_return(user)
end

it 'can view their subscription and donation history' do
5.times do
donation = FactoryBot.create(:donation, user_id: user.id)
FactoryBot.create(:subscription_donation, subscription_id: subscription.id, donation_id: donation.id)
end
visit user_path(user)
expect(page).to have_content(user.name)
expect(page).to have_content(user.email)

expect(page).to have_content(subscription.plan.name)
before(:each) do
allow_any_instance_of(SessionProvider).to receive(:current_user).and_return(user)
end

user.donations.each do |donation|
expect(page).to have_content(donation.amount)
end
it 'can view their subscription and donation history' do
5.times do
donation = FactoryBot.create(:donation, user_id: user.id)
FactoryBot.create(:subscription_donation, subscription_id: subscription.id, donation_id: donation.id)
end
visit user_latest_donations_path(user)
expect(page).to have_content('Your Donations History')

it 'can cancel an active subscription' do
expect(subscription.active).to eq(true)
visit user_path(user)
expect(page).to have_content(user.name)
expect(page).to have_content(user.email)
user.donations.each do |donation|
expect(page).to have_content(donation.amount)
end
end

expect(page).to have_content(subscription.plan.name)
it 'can cancel an active subscription' do
expect(subscription.active).to eq(true)
visit user_current_subscription_path(user)
expect(page).to have_content("You're subscribed")

click_button 'Cancel Subscription'
expect(page).to have_content(subscription.plan.name)

expect(page).to have_content('Do you want to terminate your current subscription?')
within '#cancel-subscription-dialog' do
click_button 'Cancel Subscription'
end
click_button 'Cancel Subscription'

expect(page).to have_content('No active subscription')
subscription.reload
expect(subscription.active).to eq(false)
expect(page).to have_content('Do you want to terminate your current subscription?')
within '#cancel-subscription-dialog' do
click_button 'Cancel Subscription'
end

expect(page).to have_content('No active subscription')
subscription.reload
expect(subscription.active).to eq(false)
end
end

0 comments on commit 5a67fab

Please sign in to comment.