mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
interface to create new coupons
This commit is contained in:
parent
ee03f1a79c
commit
7c434db09a
54
app/assets/javascripts/controllers/admin/coupons.coffee
Normal file
54
app/assets/javascripts/controllers/admin/coupons.coffee
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
##
|
||||||
|
# Controller used in the coupon creation page
|
||||||
|
##
|
||||||
|
Application.Controllers.controller "NewCouponController", ["$scope", "$state",'Coupon', 'growl', '_t'
|
||||||
|
, ($scope, $state, Coupon, growl, _t) ->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### PUBLIC SCOPE ###
|
||||||
|
|
||||||
|
## Values for the coupon currently created
|
||||||
|
$scope.coupon =
|
||||||
|
active: true
|
||||||
|
|
||||||
|
## Default parameters for AngularUI-Bootstrap datepicker (used for coupon validity limit selection)
|
||||||
|
$scope.datePicker =
|
||||||
|
format: Fablab.uibDateFormat
|
||||||
|
opened: false # default: datePicker is not shown
|
||||||
|
options:
|
||||||
|
startingDay: Fablab.weekStartingDay
|
||||||
|
|
||||||
|
##
|
||||||
|
# Shows the validity limit datepicker
|
||||||
|
# @param $event {Object} jQuery event object
|
||||||
|
##
|
||||||
|
$scope.openDatePicker = ($event) ->
|
||||||
|
$event.preventDefault()
|
||||||
|
$event.stopPropagation()
|
||||||
|
$scope.datePicker.opened = true
|
||||||
|
|
||||||
|
##
|
||||||
|
# Callback to save the new coupon in $scope.coupon and redirect the user to the listing page
|
||||||
|
##
|
||||||
|
$scope.saveCoupon = ->
|
||||||
|
Coupon.save coupon: $scope.coupon, (coupon) ->
|
||||||
|
$state.go('app.admin.pricing')
|
||||||
|
, (err)->
|
||||||
|
growl.error(_t('unable_to_create_the_coupon_an_error_occurred'))
|
||||||
|
console.error(err)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Controller used in the coupon edition page
|
||||||
|
##
|
||||||
|
Application.Controllers.controller "EditCouponController", ["$scope", 'Coupon', '_t'
|
||||||
|
, ($scope, Coupon, _t) ->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### PUBLIC SCOPE ###
|
||||||
|
$scope.test = 'edit'
|
||||||
|
]
|
@ -319,9 +319,9 @@ Application.Controllers.controller "EditPricingController", ["$scope", "$state",
|
|||||||
$scope.getCouponStatus = (coupon) ->
|
$scope.getCouponStatus = (coupon) ->
|
||||||
unless coupon.active
|
unless coupon.active
|
||||||
return _t('disabled')
|
return _t('disabled')
|
||||||
unless moment(coupon.valid).isSameOrBefore()
|
if coupon.valid_until and moment(coupon.valid_until).isAfter()
|
||||||
return _t('expired')
|
return _t('expired')
|
||||||
unless coupon.usages <= coupon.max_usages
|
if coupon.max_usages and coupon.usages <= coupon.max_usages
|
||||||
return _t('sold_out')
|
return _t('sold_out')
|
||||||
return _t('active')
|
return _t('active')
|
||||||
|
|
||||||
|
@ -790,7 +790,7 @@ angular.module('application.router', ['ui.router']).
|
|||||||
]
|
]
|
||||||
|
|
||||||
# coupons
|
# coupons
|
||||||
.state 'app.admin.coupons.new',
|
.state 'app.admin.coupons_new',
|
||||||
url: '/admin/coupons/new'
|
url: '/admin/coupons/new'
|
||||||
views:
|
views:
|
||||||
'main@':
|
'main@':
|
||||||
@ -798,9 +798,9 @@ angular.module('application.router', ['ui.router']).
|
|||||||
controller: 'NewCouponController'
|
controller: 'NewCouponController'
|
||||||
resolve:
|
resolve:
|
||||||
translations: [ 'Translations', (Translations) ->
|
translations: [ 'Translations', (Translations) ->
|
||||||
Translations.query(['app.admin.coupons.new', 'app.shared.coupon']).$promise
|
Translations.query(['app.admin.coupons_new', 'app.shared.coupon']).$promise
|
||||||
]
|
]
|
||||||
.state 'app.admin.coupons.edit',
|
.state 'app.admin.coupons_edit',
|
||||||
url: '/admin/coupons/:id/edit'
|
url: '/admin/coupons/:id/edit'
|
||||||
views:
|
views:
|
||||||
'main@':
|
'main@':
|
||||||
@ -808,7 +808,7 @@ angular.module('application.router', ['ui.router']).
|
|||||||
controller: 'EditCouponController'
|
controller: 'EditCouponController'
|
||||||
resolve:
|
resolve:
|
||||||
translations: [ 'Translations', (Translations) ->
|
translations: [ 'Translations', (Translations) ->
|
||||||
Translations.query(['app.admin.coupons.edit', 'app.shared.coupon']).$promise
|
Translations.query(['app.admin.coupons_edit', 'app.shared.coupon']).$promise
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
72
app/assets/templates/admin/coupons/_form.html.erb
Normal file
72
app/assets/templates/admin/coupons/_form.html.erb
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<div class="form-group" ng-class="{'has-error': couponForm['coupon[name]'].$dirty && couponForm['coupon[name]'].$invalid}">
|
||||||
|
<label for="coupon[name]">{{ 'name' | translate }} *</label>
|
||||||
|
<input type="text" id="coupon[name]"
|
||||||
|
name="coupon[name]"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="coupon.name"
|
||||||
|
required="required"/>
|
||||||
|
<span class="help-block error" ng-show="couponForm['coupon[name]'].$dirty && couponForm['coupon[name]'].$error.required" translate>{{ 'name_is_required' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" ng-class="{'has-error': couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$invalid}">
|
||||||
|
<label for="coupon[code]">{{ 'code' | translate }} *</label>
|
||||||
|
<input type="text" id="coupon[code]"
|
||||||
|
name="coupon[code]"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="coupon.code"
|
||||||
|
ng-pattern="/^[A-Z0-9]+$/"
|
||||||
|
required="required"/>
|
||||||
|
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.required" translate>{{ 'code_is_required' }}</span>
|
||||||
|
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.pattern" translate>{{ 'code_must_be_composed_of_capital_letters_and_or_digits' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" ng-class="{'has-error': couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$invalid}">
|
||||||
|
<label for="coupon[percent_off]">{{ 'percent_off' | translate }} *</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" id="coupon[percent_off]"
|
||||||
|
name="coupon[percent_off]"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="coupon.percent_off"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
required="required"/>
|
||||||
|
<span class="input-group-addon"><i class="fa fa-percent"></i></span>
|
||||||
|
</div>
|
||||||
|
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'percent_off_is_required' }}</span>
|
||||||
|
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'percentage_must_be_between_0_and_100' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="coupon[valid_until]" translate>{{ 'valid_until' }}</label>
|
||||||
|
<input type="text" id="coupon[valid_until]"
|
||||||
|
name="coupon[valid_until]"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="coupon.valid_until"
|
||||||
|
uib-datepicker-popup="{{datePicker.format}}"
|
||||||
|
datepicker-options="datePicker.options"
|
||||||
|
is-open="datePicker.opened"
|
||||||
|
ng-click="openDatePicker($event)"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" ng-class="{'has-error': couponForm['coupon[max_usages]'].$dirty && couponForm['coupon[max_usages]'].$invalid}">
|
||||||
|
<label for="coupon[max_usages]">{{ 'max_usages' | translate }}</label>
|
||||||
|
<input type="number" id="coupon[max_usages]"
|
||||||
|
name="coupon[max_usages]"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="coupon.max_usages"
|
||||||
|
min="0"/>
|
||||||
|
<span class="help-block error" ng-show="couponForm['coupon[max_usages]'].$dirty && couponForm['coupon[max_usages]'].$error.min" translate>{{ 'max_usages_must_be_equal_or_greater_than_0' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="coupon[active]" translate>{{ 'enabled' }}</label>
|
||||||
|
<input bs-switch
|
||||||
|
ng-model="coupon.active"
|
||||||
|
id="coupon[active]"
|
||||||
|
type="checkbox"
|
||||||
|
class="form-control"
|
||||||
|
switch-on-text="{{ 'yes' | translate }}"
|
||||||
|
switch-off-text="{{ 'no' | translate }}"
|
||||||
|
switch-animate="true" />
|
||||||
|
<input type="hidden" name="coupon[active]" value="{{coupon.active}}"/>
|
||||||
|
</div>
|
@ -1 +1,40 @@
|
|||||||
<ng-include src="'<%= asset_path 'admin/coupons/_coupon.html' %>'"></ng-include>
|
<section class="heading b-b">
|
||||||
|
<div class="row no-gutter">
|
||||||
|
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||||
|
<section class="heading-btn">
|
||||||
|
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||||
|
<section class="heading-title">
|
||||||
|
<h1>{{ 'coupon' | translate }} {{ coupon.name }}</h1>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3">
|
||||||
|
<section class="heading-actions wrapper">
|
||||||
|
<a class="btn btn-lg btn-block btn-default m-t-xs" ui-sref="app.admin.pricing" translate>{{ 'cancel' }}</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="row no-gutter">
|
||||||
|
<div class=" col-sm-12 col-md-9 b-r nopadding">
|
||||||
|
|
||||||
|
<div id="couponForm">
|
||||||
|
<form name="couponForm" novalidate="novalidate" class="col-lg-7 col-lg-offset-2 m-t-lg form-group">
|
||||||
|
|
||||||
|
<ng-include src="'<%= asset_path 'admin/coupons/_form.html' %>'"></ng-include>
|
||||||
|
|
||||||
|
<div class="panel-footer no-padder">
|
||||||
|
<input type="submit" value="{{ 'confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="couponForm.$invalid"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1 +1,33 @@
|
|||||||
<ng-include src="'<%= asset_path 'admin/coupons/_coupon.html' %>'"></ng-include>
|
<section class="heading b-b">
|
||||||
|
<div class="row no-gutter">
|
||||||
|
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||||
|
<section class="heading-btn">
|
||||||
|
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||||
|
<section class="heading-title">
|
||||||
|
<h1 translate>{{ 'add_a_coupon' }}</h1>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="row no-gutter">
|
||||||
|
<div class=" col-sm-12 col-md-9 b-r nopadding">
|
||||||
|
|
||||||
|
<div id="couponForm">
|
||||||
|
<form name="couponForm" novalidate="novalidate" class="col-lg-10 col-lg-offset-2 m-t-lg form-group">
|
||||||
|
|
||||||
|
<ng-include src="'<%= asset_path 'admin/coupons/_form.html' %>'"></ng-include>
|
||||||
|
|
||||||
|
<div class="panel-footer no-padder">
|
||||||
|
<input type="button" value="{{ 'save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="couponForm.$invalid" ng-click="saveCoupon()"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<h2 translate>{{ 'list_of_the_coupons' }}</h2>
|
<h2 translate>{{ 'list_of_the_coupons' }}</h2>
|
||||||
|
|
||||||
<button type="button" class="btn btn-warning m-t-lg m-b" ui-sref="app.admin.coupons.new" translate>{{ 'add_a_new_coupon' }}</button>
|
<button type="button" class="btn btn-warning m-t-lg m-b" ui-sref="app.admin.coupons_new" translate>{{ 'add_a_new_coupon' }}</button>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'name' | translate }}</th>
|
<th translate>{{ 'name' }}</th>
|
||||||
<th>{{ 'percentage_off' | translate }}</th>
|
<th translate>{{ 'percentage_off' }}</th>
|
||||||
<th>{{ 'status' | translate }}</th>
|
<th translate>{{ 'status' }}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<td>{{coupon.percent_off}} %</td>
|
<td>{{coupon.percent_off}} %</td>
|
||||||
<td>{{getCouponStatus(coupon)}}</td>
|
<td>{{getCouponStatus(coupon)}}</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-default" ui-sref="app.admin.coupons.edit({id:coupon.id})"><i class="fa fa-pencil-square-o"></i></button>
|
<button type="button" class="btn btn-default" ui-sref="app.admin.coupons_edit({id:coupon.id})"><i class="fa fa-pencil-square-o"></i></button>
|
||||||
<button type="button" class="btn btn-danger" ng-click="deleteCoupon(coupons, coupon.id)"><i class="fa fa-trash"></i></button>
|
<button type="button" class="btn btn-danger" ng-click="deleteCoupon(coupons, coupon.id)"><i class="fa fa-trash"></i></button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
class Coupon < ActiveRecord::Base
|
class Coupon < ActiveRecord::Base
|
||||||
has_many :invoices
|
has_many :invoices
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
validates :code, presence: true
|
validates :code, presence: true
|
||||||
validates :code, format: { with: /[A-Z0-9]+/ ,message: 'only caps letters and numbers'}
|
validates :code, format: { with: /\A[A-Z0-9]+\z/ ,message: 'only caps letters and numbers'}
|
||||||
validates :percent_off, presence: true
|
validates :percent_off, presence: true
|
||||||
validates :percent_off, :inclusion => 0..100
|
validates :percent_off, :inclusion => 0..100
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
json.extract! coupon, :id, :name, :code, :percent_off, :valid_until, :max_usages, :active, :created_at
|
json.extract! coupon, :id, :name, :code, :percent_off, :valid_until, :max_usages, :active, :created_at
|
||||||
json.usages @coupon.invoices.count
|
json.usages coupon.invoices.count
|
@ -157,6 +157,11 @@ fr:
|
|||||||
coupon_was_successfully_deleted: "Le code promotionnel a bien été supprimé."
|
coupon_was_successfully_deleted: "Le code promotionnel a bien été supprimé."
|
||||||
unable_to_delete_the_specified_coupon_an_error_occurred: "Impossible de supprimer le code promotionnel, une erreur s'est produite."
|
unable_to_delete_the_specified_coupon_an_error_occurred: "Impossible de supprimer le code promotionnel, une erreur s'est produite."
|
||||||
|
|
||||||
|
coupons_new:
|
||||||
|
# ajouter un code promotionnel
|
||||||
|
add_a_coupon: "Ajouter un code promotionnel"
|
||||||
|
unable_to_create_the_coupon_an_error_occurred: "Impossible de créer le code promotionnel : une erreur est survenue."
|
||||||
|
|
||||||
plans:
|
plans:
|
||||||
new:
|
new:
|
||||||
# ajouter une formule d'abonnement sur la plate-forme
|
# ajouter une formule d'abonnement sur la plate-forme
|
||||||
|
@ -331,3 +331,15 @@ fr:
|
|||||||
debit_reservation_training: "Payer un reservation de formation"
|
debit_reservation_training: "Payer un reservation de formation"
|
||||||
debit_reservation_machine: "Payer un reservation de machine"
|
debit_reservation_machine: "Payer un reservation de machine"
|
||||||
debit_reservation_event: "Payer un reservation d'évenement"
|
debit_reservation_event: "Payer un reservation d'évenement"
|
||||||
|
|
||||||
|
coupon:
|
||||||
|
code: "Code"
|
||||||
|
code_is_required: "Le code est requis."
|
||||||
|
code_must_be_composed_of_capital_letters_and_or_digits: "Le code doit être composé de lettres majuscules et/ou de chiffres."
|
||||||
|
percent_off: "Pourcentage de réduction"
|
||||||
|
percent_off_is_required: "Le pourcentage de réduction est requis."
|
||||||
|
percentage_must_be_between_0_and_100: "Le pourcentage doit être compris entre 0 et 100."
|
||||||
|
valid_until: "Valable jusqu'au (inclus)"
|
||||||
|
max_usages: "Nombre maximum d'utilisations autorisées"
|
||||||
|
max_usages_must_be_equal_or_greater_than_0: "Le nombre d'utilisations maximum doit être supérieur ou égal à 0."
|
||||||
|
enabled: "Activé"
|
Loading…
x
Reference in New Issue
Block a user