1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

Merge branch 'dev' for release 4.6.0

This commit is contained in:
Sylvain 2020-10-20 16:44:50 +02:00
commit f5500d83b2
424 changed files with 9907 additions and 2107 deletions

1
.browserslistrc Normal file
View File

@ -0,0 +1 @@
defaults

View File

@ -16,6 +16,7 @@ redis
# Ignore public assets
public/uploads
public/assets
public/packs
# Ignore all logfiles and tempfiles.
log
@ -35,6 +36,9 @@ exports
# CSV imports
imports
# accounting archives
accounting
.DS_Store
# Development files

8
.gitignore vendored
View File

@ -18,6 +18,8 @@
/coverage
/public/uploads
/public/packs
/public/packs-test
/public/assets
/public/api
@ -38,7 +40,7 @@
# CSV imports
/imports/*
# Archives of cLosed accounting periods
# Archives of closed accounting periods
/accounting/*
.DS_Store
@ -56,3 +58,7 @@
/node_modules
npm-debug.log
yarn-error.log
/yarn-error.log
yarn-debug.log*
.yarn-integrity

2
.nvmrc
View File

@ -1 +1 @@
10.13.0
12.18.3

4
.rspec
View File

@ -1,4 +0,0 @@
--color
--require spec_helper
--format documentation
--backtrace

View File

@ -1,34 +0,0 @@
Fab-manager uses some external components, which are licenced under the
terms of the following licences:
- [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0):
- [jasny-bootstrap](https://github.com/jasny/bootstrap/)
- [elasticsearch](https://github.com/elasticsearch/bower-elasticsearch-js)
- [nvd3](https://github.com/novus/nvd3)
- [angular-bootstrap-switch](https://github.com/frapontillo/angular-bootstrap-switch)
- [elasticsearch-rails](https://github.com/elastic/elasticsearch-rails)
- [elasticsearch-model](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-model)
- [elasticsearch-persistence](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence)
- font [Open Sans](http://www.fontsquirrel.com/fonts/open-sans)
- [General Public License version 2](http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.en.html):
- [railroady](https://github.com/preston/railroady)
- [unicorn](https://github.com/defunkt/unicorn)
- [prawn](https://github.com/prawnpdf/prawn)
- [prawn-table](https://github.com/prawnpdf/prawn-table)
- [BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause)
- [ruby](https://www.ruby-lang.org)
- [rubyzip](https://github.com/rubyzip/rubyzip)
- [byebug](https://github.com/deivid-rodriguez/byebug)
- [MIT Licence](https://opensource.org/licenses/MIT)
- Errors and omissions excepted, all the other external libraries used
in this project.
Please refer to the libraries documentation for more information about
their licences.
Complete lists of used libraries are available in `package.json` for the
JS/EcmaScript libraries and in `Gemfile` for Ruby libraries.

View File

@ -1,5 +1,24 @@
# Changelog Fab-manager
## v4.6.0 2020 October 20
- Migrated the assets build pipeline from Sprockets to Webpack
- Version check during the upgrade
- Fix a bug: changing the date of a training session does not prevent the selection of a different type of training
- Fix a bug: unable to change the date formats using the setup script
- Fix a bug: missing translation for projets drafts in public profile
- Fix a bug: email notification after reservation update have wrong previous date (#234)
- Fix a bug: unable to rename a group containing users
- Updated contribution guidelines
- Updated summernote to 0.8.18
- Updated angular-summernote to 0.8.1
- Updated FontAwesome from v4 to v5
- Updated jquery-minicolors to 2.3.5
- Updated angular-bootstrap-switch to 0.5.2
- Updated bootstrap-switch to 3.4.0
- Updated fullCalendar to 3.10.2
- [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/mount-webpack.sh | bash`
## v4.5.9 2020 September 29
- Ability to configure until when the events are shown on the home page

View File

@ -56,7 +56,7 @@ Example:
> 2. This is the second step
> 3. Further steps, etc.
>
> `<url>` - a link to the reduced test case
> `<url>` - a link to your instance, demonstrating the problem
>
> Any other information you want to share that is relevant to the issue being reported. This might include the lines of
> code that you have identified as causing the bug, and potential solutions (and your opinions on their merits).
@ -69,8 +69,8 @@ Feature requests are welcome. But take a moment to find out whether your idea fi
project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature.
Please provide as much detail and context as possible.
Please note also that [the forum](https://forum.fab-manager.com) is probably a better place for discussing about feature
requests.
Please note also that [the feedback tool](https://feedback.fab-manager.com) is probably a better place for discussing
about feature requests.
<a name="pull-requests"></a>
@ -132,3 +132,20 @@ Adhering to the following process is the best way to get your work included in t
**IMPORTANT**: By submitting a patch, you agree to allow the project owners to license your work under the terms of
the [GNU Affero General Public License](LICENSE.md).
<a name="translations"></a>
## Translations
Event without any technical skills, you can contribute to translate Fab-manager to your local language.
This is possible using a [Translation Management System](https://en.wikipedia.org/wiki/Translation_management_system)
known as [Crowdin](https://crowdin.com/). This tool helps the community to contribute to the translation of Fab-Manager
into their favorite local languages.
You can access it at [translate.fab-manager.com](https://translate.fab-manager.com/) and start translating.
If you want to translate Fab-Manager to a new unlisted language, just post a comment [on the forum](https://forum.fab-manager.com/)
or [create an issue on GitHub](https://github.com/sleede/fab-manager/issues) and we'll be happy to add it
to the list of supported languages.
If you know where an untranslated sentence is located and you can't find it on Crowdin, you can use
the "in-context translation": [in-context.translate.fab-manager.com](https://in-context.translate.fab-manager.com/).
From there, you'll be able to browse the application interface and select in one click the sentences you want to translate.

View File

View File

@ -44,7 +44,7 @@ RUN bundle install --binstubs --without development test doc
WORKDIR /usr/src/app
COPY package.json /usr/src/app/package.json
COPY yarn.lock /usr/src/app/yarn.lock
RUN yarn install --production
RUN yarn install
# Clean up build deps, cached packages and temp files
RUN apk del .build-deps && \
@ -62,7 +62,7 @@ RUN mkdir -p /usr/src/app && \
mkdir -p /usr/src/app/imports && \
mkdir -p /usr/src/app/log && \
mkdir -p /usr/src/app/public/uploads && \
mkdir -p /usr/src/app/public/assets && \
mkdir -p /usr/src/app/public/packs && \
mkdir -p /usr/src/app/accounting && \
mkdir -p /usr/src/app/tmp/sockets && \
mkdir -p /usr/src/app/tmp/pids
@ -76,7 +76,7 @@ VOLUME /usr/src/app/exports
VOLUME /usr/src/app/imports
VOLUME /usr/src/app/public
VOLUME /usr/src/app/public/uploads
VOLUME /usr/src/app/public/assets
VOLUME /usr/src/app/public/packs
VOLUME /usr/src/app/accounting
VOLUME /var/log/supervisor

20
Gemfile
View File

@ -2,29 +2,18 @@
source 'https://rubygems.org'
git 'https://github.com/judynjagi/compass.git', branch: 'stable' do
gem 'compass-core'
end
gem 'compass-rails', '3.1.0'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.4'
# Used by rails 5.2 to reduce the app boot time by over 50%
gem 'bootsnap'
# Use Puma as web server
gem 'puma', '3.12.6'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0', '>= 5.0.6'
gem 'webpacker', '~> 5.x'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 4.1.20'
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
gem 'jbuilder_cache_multi'
gem "json", ">= 2.3.0"
gem 'json', '>= 2.3.0'
gem 'forgery'
gem 'responders', '~> 2.0'
@ -72,6 +61,7 @@ gem 'seed_dump'
gem 'pg'
gem 'pg_search'
# authentication
gem 'devise', '>= 4.6.0'
gem 'omniauth', '~> 1.9.0'
@ -80,11 +70,9 @@ gem 'omniauth-rails_csrf_protection', '~> 0.1'
gem 'rolify'
# pagination
gem 'kaminari'
gem 'bootstrap-sass', '>= 3.4.1'
gem 'font-awesome-rails'
# Image processing ruby wrapper for ImageMagick
gem 'mini_magick'
# upload files

View File

@ -1,21 +1,3 @@
GIT
remote: https://github.com/judynjagi/compass.git
revision: 1692c0a7fd8ada392ad5f524bef756ab74e300d0
branch: stable
specs:
compass (1.0.3)
chunky_png (~> 1.2)
compass-core (~> 1.0.2)
compass-import-once (~> 1.0.5)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
sass (>= 3.3.13, < 3.5)
compass-core (1.0.2)
multi_json (~> 1.0)
sass (>= 3.3.0, < 3.5)
compass-import-once (1.0.5)
sass (>= 3.2, < 3.5)
GEM
remote: https://rubygems.org/
specs:
@ -75,8 +57,6 @@ GEM
rails (>= 4.1)
arel (9.0.0)
ast (2.4.0)
autoprefixer-rails (9.7.4)
execjs
awesome_print (1.8.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
@ -86,9 +66,6 @@ GEM
bindex (0.8.1)
bootsnap (1.4.6)
msgpack (~> 1.0)
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
builder (3.2.4)
camertron-eprun (1.1.1)
carrierwave (2.1.0)
@ -107,15 +84,10 @@ GEM
actionpack (>= 3.1)
caxlsx (>= 3.0)
chroma (0.2.0)
chunky_png (1.3.11)
cldr-plurals-runtime-rb (1.0.1)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
compass-rails (3.1.0)
compass (~> 1.0.0)
sass-rails (< 5.1)
sprockets (< 4.0)
concurrent-ruby (1.1.7)
concurrent-ruby (1.1.6)
connection_pool (2.2.3)
coveralls_reborn (0.18.0)
simplecov (>= 0.18.1, < 0.20.0)
@ -163,14 +135,11 @@ GEM
erubi (1.9.0)
et-orbi (1.2.1)
tzinfo
execjs (2.7.0)
faker (2.10.2)
i18n (>= 1.6, < 2)
faraday (0.17.3)
multipart-post (>= 1.2, < 3)
ffi (1.12.2)
font-awesome-rails (4.7.0.5)
railties (>= 3.2, < 6.1)
foreman (0.87.0)
forgery (0.7.0)
friendly_id (5.1.0)
@ -201,10 +170,6 @@ GEM
activesupport (>= 5.0.0)
jbuilder_cache_multi (0.1.0)
jbuilder (>= 1.5.0, < 3)
jquery-rails (4.3.5)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.3.1)
jwt (2.2.1)
kaminari (1.2.1)
@ -303,6 +268,8 @@ GEM
rack (2.2.3)
rack-protection (2.0.8.1)
rack
rack-proxy (0.6.5)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
railroady (1.5.3)
@ -369,18 +336,10 @@ GEM
rubyzip (>= 1.3.0)
rubyzip (1.3.0)
safe_yaml (1.0.5)
sass (3.4.25)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sassc (2.2.1)
ffi (~> 1.9)
seed_dump (3.3.1)
activerecord (>= 4)
activesupport (>= 4)
semantic_range (2.3.0)
sha3 (1.0.1)
sidekiq (6.0.7)
connection_pool (>= 2.2.2)
@ -418,7 +377,6 @@ GEM
tins (~> 1.0)
thor (0.20.3)
thread_safe (0.3.6)
tilt (2.0.10)
tins (1.25.0)
sync
ttfunk (1.5.1)
@ -428,8 +386,6 @@ GEM
tzinfo
tzinfo (1.2.7)
thread_safe (~> 0.1)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.4.1)
vcr (3.0.1)
virtus (1.0.5)
@ -448,6 +404,11 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.2.1)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
@ -463,13 +424,10 @@ DEPENDENCIES
apipie-rails
awesome_print
bootsnap
bootstrap-sass (>= 3.4.1)
carrierwave
caxlsx
caxlsx_rails
chroma
compass-core!
compass-rails (= 3.1.0)
coveralls_reborn (~> 0.18.0)
database_cleaner
devise (>= 4.6.0)
@ -479,14 +437,12 @@ DEPENDENCIES
elasticsearch-rails (~> 5)
faker
faraday (~> 0.17)
font-awesome-rails
foreman
forgery
friendly_id (~> 5.1.0)
icalendar
jbuilder (~> 2.5)
jbuilder_cache_multi
jquery-rails
json (>= 2.3.0)
kaminari
listen (~> 3.0.5)
@ -518,7 +474,6 @@ DEPENDENCIES
rubocop (~> 0.61.1)
rubyXL
rubyzip (>= 1.3.0)
sass-rails (~> 5.0, >= 5.0.6)
seed_dump
sha3
sidekiq (>= 6.0.7)
@ -528,10 +483,10 @@ DEPENDENCIES
spring-watcher-listen (~> 2.0.0)
stripe (= 5.1.1)
sys-filesystem
uglifier (>= 4.1.20)
vcr (= 3.0.1)
web-console (>= 3.3.0)
webmock
webpacker (~> 5.x)
BUNDLED WITH
2.1.4

View File

@ -1,4 +1,4 @@
Copyright (C) 2019 Sleede
Copyright (C) 2020 Sleede
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,87 +0,0 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require polyfill
//= require angular
//= require angular-cookies
//= require angular-resource
//= require angular-sanitize
//= require angular-cookies
//= require angular-touch
//= require @uirouter/angularjs/release/angular-ui-router
//= require angular-ui-bootstrap/dist/ui-bootstrap-tpls
//= require ui-select/dist/select
//= require moment/moment
//= require moment-timezone/builds/moment-timezone-with-data-2012-2022
//= require angular-ui-calendar/src/calendar
//= require fullcalendar/dist/fullcalendar
//= require angular-moment/angular-moment
//= require ngUpload/ng-upload
//= require jasny-bootstrap/js/fileinput
//= require holderjs/holder
//= require AngularDevise/lib/devise
//= require devise-modal
//= require angular-growl-v2/build/angular-growl
//= require angular-xeditable/dist/js/xeditable
//= require checklist-model/checklist-model
//= require angular-unsavedchanges/lib/unsavedChanges
//= require angular-loading-bar/src/loading-bar
//= require angular-scroll/angular-scroll
//= require angular-google-analytics/dist/angular-google-analytics
//= require dirDisqus
//= require humanize
//= require underscore/underscore
//= require elasticsearch-browser/elasticsearch.angular
//= require d3/d3
//= require nvd3/build/nv.d3.js
//= require twitter-fetcher
//= require app
//= require router
//= require medium-editor/dist/js/medium-editor
//= require angular-medium-editor/dist/angular-medium-editor
//= require bootstrap-switch/dist/js/bootstrap-switch.min
//= require angular-bootstrap-switch/dist/angular-bootstrap-switch.min
//= require angular-base64-upload/dist/angular-base64-upload.min
//= require summernote/dist/summernote
//= require angular-summernote/dist/angular-summernote
//= require summernote-ext-nugget
//= require jquery-minicolors/jquery.minicolors.js
//= require angular-minicolors/angular-minicolors.js
//= require angular-translate/dist/angular-translate
//= require angular-translate-loader-partial/angular-translate-loader-partial
//= require messageformat/messageformat
//= require angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat
//= require ng-fittext/dist/ng-FitText.min
//= require angular-aside/dist/js/angular-aside
//= require ng-caps-lock/ng-caps-lock
//= require angular-recaptcha
//= require codemirror/lib/codemirror
//= require codemirror/addon/edit/matchbrackets
//= require codemirror/mode/css/css
//= require codemirror/mode/sass/sass
//= require angular-ui-codemirror/src/ui-codemirror
//= require angular-hotkeys/build/hotkeys
//= require hone/dist/hone
//= require tether/dist/js/tether
//= require angular-bind-html-compile/angular-bind-html-compile
//= require angular-ui-tour/dist/angular-ui-tour
//= require_tree ./controllers
//= require_tree ./services
//= require_tree ./directives
//= require_tree ./filters
<%
PluginRegistry.javascripts.each { |js| require_asset(js) }
%>

View File

@ -1,122 +0,0 @@
/**
* Created by sylvain on 20/08/14.
*
* Based on http://jsperf.com/diacritics/12
*/
Application.Services.service('Diacritics', [
'$resource', function ($resource) {
return {
/**
* Fast diacritics removing function, works on the provided string.
* Eg. "éêè çä bönjoür" will become "eee ca bonjour"
* @param str {string} to ascii-fy
* @returns {string} without diacritics
*/
remove: function (str) {
var defaultDiacriticsRemovalap = [
{ 'base': 'A', 'letters': '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F' },
{ 'base': 'AA', 'letters': '\uA732' },
{ 'base': 'AE', 'letters': '\u00C6\u01FC\u01E2' },
{ 'base': 'AO', 'letters': '\uA734' },
{ 'base': 'AU', 'letters': '\uA736' },
{ 'base': 'AV', 'letters': '\uA738\uA73A' },
{ 'base': 'AY', 'letters': '\uA73C' },
{ 'base': 'B', 'letters': '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181' },
{ 'base': 'C', 'letters': '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E' },
{ 'base': 'D', 'letters': '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779' },
{ 'base': 'DZ', 'letters': '\u01F1\u01C4' },
{ 'base': 'Dz', 'letters': '\u01F2\u01C5' },
{ 'base': 'E', 'letters': '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E' },
{ 'base': 'F', 'letters': '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B' },
{ 'base': 'G', 'letters': '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E' },
{ 'base': 'H', 'letters': '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D' },
{ 'base': 'I', 'letters': '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197' },
{ 'base': 'J', 'letters': '\u004A\u24BF\uFF2A\u0134\u0248' },
{ 'base': 'K', 'letters': '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2' },
{ 'base': 'L', 'letters': '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780' },
{ 'base': 'LJ', 'letters': '\u01C7' },
{ 'base': 'Lj', 'letters': '\u01C8' },
{ 'base': 'M', 'letters': '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C' },
{ 'base': 'N', 'letters': '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4' },
{ 'base': 'NJ', 'letters': '\u01CA' },
{ 'base': 'Nj', 'letters': '\u01CB' },
{ 'base': 'O', 'letters': '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C' },
{ 'base': 'OI', 'letters': '\u01A2' },
{ 'base': 'OO', 'letters': '\uA74E' },
{ 'base': 'OU', 'letters': '\u0222' },
{ 'base': 'OE', 'letters': '\u008C\u0152' },
{ 'base': 'oe', 'letters': '\u009C\u0153' },
{ 'base': 'P', 'letters': '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754' },
{ 'base': 'Q', 'letters': '\u0051\u24C6\uFF31\uA756\uA758\u024A' },
{ 'base': 'R', 'letters': '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782' },
{ 'base': 'S', 'letters': '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784' },
{ 'base': 'T', 'letters': '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786' },
{ 'base': 'TZ', 'letters': '\uA728' },
{ 'base': 'U', 'letters': '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244' },
{ 'base': 'V', 'letters': '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245' },
{ 'base': 'VY', 'letters': '\uA760' },
{ 'base': 'W', 'letters': '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72' },
{ 'base': 'X', 'letters': '\u0058\u24CD\uFF38\u1E8A\u1E8C' },
{ 'base': 'Y', 'letters': '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE' },
{ 'base': 'Z', 'letters': '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762' },
{ 'base': 'a', 'letters': '\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250' },
{ 'base': 'aa', 'letters': '\uA733' },
{ 'base': 'ae', 'letters': '\u00E6\u01FD\u01E3' },
{ 'base': 'ao', 'letters': '\uA735' },
{ 'base': 'au', 'letters': '\uA737' },
{ 'base': 'av', 'letters': '\uA739\uA73B' },
{ 'base': 'ay', 'letters': '\uA73D' },
{ 'base': 'b', 'letters': '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253' },
{ 'base': 'c', 'letters': '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184' },
{ 'base': 'd', 'letters': '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A' },
{ 'base': 'dz', 'letters': '\u01F3\u01C6' },
{ 'base': 'e', 'letters': '\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD' },
{ 'base': 'f', 'letters': '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C' },
{ 'base': 'g', 'letters': '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F' },
{ 'base': 'h', 'letters': '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265' },
{ 'base': 'hv', 'letters': '\u0195' },
{ 'base': 'i', 'letters': '\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131' },
{ 'base': 'j', 'letters': '\u006A\u24D9\uFF4A\u0135\u01F0\u0249' },
{ 'base': 'k', 'letters': '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3' },
{ 'base': 'l', 'letters': '\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747' },
{ 'base': 'lj', 'letters': '\u01C9' },
{ 'base': 'm', 'letters': '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F' },
{ 'base': 'n', 'letters': '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5' },
{ 'base': 'nj', 'letters': '\u01CC' },
{ 'base': 'o', 'letters': '\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275' },
{ 'base': 'oi', 'letters': '\u01A3' },
{ 'base': 'ou', 'letters': '\u0223' },
{ 'base': 'oo', 'letters': '\uA74F' },
{ 'base': 'p', 'letters': '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755' },
{ 'base': 'q', 'letters': '\u0071\u24E0\uFF51\u024B\uA757\uA759' },
{ 'base': 'r', 'letters': '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783' },
{ 'base': 's', 'letters': '\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B' },
{ 'base': 't', 'letters': '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787' },
{ 'base': 'tz', 'letters': '\uA729' },
{ 'base': 'u', 'letters': '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289' },
{ 'base': 'v', 'letters': '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C' },
{ 'base': 'vy', 'letters': '\uA761' },
{ 'base': 'w', 'letters': '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73' },
{ 'base': 'x', 'letters': '\u0078\u24E7\uFF58\u1E8B\u1E8D' },
{ 'base': 'y', 'letters': '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF' },
{ 'base': 'z', 'letters': '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763' }
];
var diacriticsMap = {};
for (var i = 0; i < defaultDiacriticsRemovalap.length; i++) {
var letters = defaultDiacriticsRemovalap[i].letters.split('');
for (var j = 0; j < letters.length; j++) {
diacriticsMap[letters[j]] = defaultDiacriticsRemovalap[i].base;
}
}
// eslint-disable-next-line no-control-regex
return str.replace(/[^\u0000-\u007E]/g, function (a) {
return diacriticsMap[a] || a;
});
}
};
}
]);

View File

@ -1,3 +0,0 @@
Application.Services.service('es', function (esFactory) {
return esFactory({ host: window.location.origin });
});

View File

@ -1,59 +0,0 @@
'use strict';
Application.Services.factory('Help', ['$rootScope', '$uibModal', '$state', 'AuthService',
function ($rootScope, $uibModal, $state, AuthService) {
const TOURS = {
'app.public.home': 'welcome',
'app.public.machines_list': 'machines',
'app.public.spaces_list': 'spaces',
'app.admin.trainings': 'trainings',
'app.admin.calendar': 'calendar',
'app.admin.members': 'members',
'app.admin.invoices': 'invoices',
'app.admin.pricing': 'pricing',
'app.admin.events': 'events',
'app.admin.projects': 'projects',
'app.admin.statistics': 'statistics',
'app.admin.settings': 'settings',
'app.admin.open_api_clients': 'open-api'
};
return function (e) {
if (!AuthService.isAuthorized(['admin', 'manager'])) return;
if (e.key === 'F1') {
e.preventDefault();
// retrieve the tour name, based on the current location
const tourName = TOURS[$state.current.name];
// if no tour, just open the guide
if (tourName === undefined) {
return window.open('https://github.com/sleede/fab-manager/raw/master/doc/fr/guide_utilisation_fab_manager_v4.5.pdf', '_blank');
}
$uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/help_modal.html" %>',
resolve: {
tourName: function () { return tourName; }
},
controller: ['$scope', '$uibModalInstance', 'uiTourService', 'tourName', function ($scope, $uibModalInstance, uiTourService, tourName) {
// start the tour and hide the modal
$scope.onTour = function () {
const tour = uiTourService.getTourByName(tourName);
if (tour) { tour.start(); }
$uibModalInstance.close('tour');
};
// open the user's guide and hide the modal
$scope.onGuide = function () {
$uibModalInstance.close('guide');
};
}]
});
}
};
}]);

View File

@ -1,6 +0,0 @@
'use strict';
// list the social networks supported in the user's profiles
Application.Services.factory('SocialNetworks', [function () {
return ['facebook', 'twitter', 'google_plus', 'viadeo', 'linkedin', 'instagram', 'youtube', 'vimeo', 'dailymotion', 'github', 'echosciences', 'website', 'pinterest', 'lastfm', 'flickr'];
}]);

View File

@ -1,3 +0,0 @@
/*
*= require fullcalendar/dist/fullcalendar.print
*/

View File

@ -1,43 +0,0 @@
/*
*= require_self
*= require ui-select/dist/select
*= require fullcalendar/dist/fullcalendar
*= require jasny-bootstrap/dist/css/jasny-bootstrap
*= require angular-growl-v2/build/angular-growl
*= require angular-xeditable/dist/css/xeditable
*= require angular-loading-bar/build/loading-bar
*= require nvd3/build/nv.d3
*= require font-awesome
*= require medium-editor/dist/css/medium-editor
*= require medium-editor/dist/css/themes/default
*= require bootstrap-switch/dist/css/bootstrap3/bootstrap-switch
*= require summernote/dist/summernote
*= require jquery-minicolors/jquery.minicolors.css
*= require angular-aside/dist/css/angular-aside
*= require codemirror/lib/codemirror
*/
@import "app.functions";
@import "bootstrap-compass";
@import "bootstrap-sprockets";
@import "compass";
@import "bootstrap_and_overrides";
@import "app.utilities";
@import "app.colors";
@import "app.base";
@import "app.layout";
@import "app.nav";
@import "app.buttons";
@import "app.components";
@import "app.plugins";
@import "modules/*";
@import "app.responsive";
<% PluginRegistry.stylesheets.each do |stylesheet| %>
<% basename = File.basename(stylesheet,'.scss') %>
<%= "@import '#{basename}';" %>
<% end %>

View File

@ -1,339 +0,0 @@
<div class="alert alert-warning p-md m-t" role="alert" ng-hide="modules.invoicing">
<i class="fa fa-warning m-r"></i>
<span translate>{{ 'app.admin.invoices.warning_invoices_disabled' }}</span>
</div>
<form class="invoice-placeholder">
<div class="invoice-logo">
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:&#xf03e;/font:FontAwesome/icon" bs-holder ng-if="!invoice.logo" class="img-responsive">
<img base-sixty-four-image="invoice.logo" ng-if="invoice.logo && invoice.logo.base64">
<div class="tools-box">
<div class="btn-group">
<div class="btn btn-default btn-file">
<i class="fa fa-edit"></i> {{ 'app.admin.invoices.change_logo' | translate }}
<input type="file" accept="image/png,image/jpeg,image/x-png,image/pjpeg" name="invoice[logo][attachment]" ng-model="invoice.logo" base-sixty-four-input>
</div>
</div>
</div>
</div>
<div class="invoice-buyer-infos">
<strong translate>{{ 'app.admin.invoices.john_smith' }}</strong>
<div translate>{{ 'app.admin.invoices.john_smith_at_example_com' }}</div>
</div>
<div class="invoice-reference invoice-editable" ng-click="openEditReference()">{{ 'app.admin.invoices.invoice_reference_' | translate }} {{mkReference()}}</div>
<div class="invoice-code invoice-editable" ng-show="invoice.code.active" ng-click="openEditCode()">{{ 'app.admin.invoices.code_' | translate }} {{invoice.code.model}}</div>
<div class="invoice-code invoice-activable" ng-show="!invoice.code.active" ng-click="openEditCode()" translate>{{ 'app.admin.invoices.code_disabled' }}</div>
<div class="invoice-order invoice-editable" ng-click="openEditInvoiceNb()"> {{ 'app.admin.invoices.order_num' | translate }} {{mkNumber()}}</div>
<div class="invoice-date">{{ 'app.admin.invoices.invoice_issued_on_DATE_at_TIME' | translate:{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT')} }}</div>
<div class="invoice-object">
{{ 'app.admin.invoices.object_reservation_of_john_smith_on_DATE_at_TIME' | translate:{DATE:(inOneWeek | amDateFormat:'L'), TIME:(inOneWeek | amDateFormat:'LT')} }}
</div>
<div class="invoice-data">
{{ 'app.admin.invoices.order_summary' | translate }}
<table>
<thead>
<tr>
<th translate>{{ 'app.admin.invoices.details' }}</th>
<th class="right" translate>{{ 'app.admin.invoices.amount' }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ 'app.admin.invoices.machine_booking-3D_printer' | translate }} {{inOneWeek | amDateFormat:'LLL'}} - {{inOneWeekAndOneHour | amDateFormat:'LT'}}</td>
<td class="right">{{30.0 | currency}}</td>
</tr>
<tr class="invoice-total" ng-class="{'bold vat-line':invoice.VAT.active}">
<td ng-show="!invoice.VAT.active" translate>{{ 'app.admin.invoices.total_amount' }}</td>
<td ng-show="invoice.VAT.active" translate>{{ 'app.admin.invoices.total_including_all_taxes' }}</td>
<td class="right">{{30.0 | currency}}</td>
</tr>
<tr class="invoice-vat invoice-activable" ng-click="openEditVAT()" ng-show="!invoice.VAT.active">
<td translate>{{ 'app.admin.invoices.VAT_disabled' }}</td>
<td></td>
</tr>
<tr class="invoice-vat invoice-editable vat-line italic" ng-click="openEditVAT()" ng-show="invoice.VAT.active">
<td>{{ 'app.admin.invoices.including_VAT' | translate }} {{invoice.VAT.rate}} %</td>
<td>{{30-(30/(invoice.VAT.rate/100+1)) | currency}}</td>
</tr>
<tr class="invoice-ht vat-line italic" ng-show="invoice.VAT.active">
<td translate>{{ 'app.admin.invoices.including_total_excluding_taxes' }}</td>
<td>{{30/(invoice.VAT.rate/100+1) | currency}}</td>
</tr>
<tr class="invoice-payed vat-line bold" ng-show="invoice.VAT.active">
<td translate>{{ 'app.admin.invoices.including_amount_payed_on_ordering' }}</td>
<td>{{30.0 | currency}}</td>
</tr>
</tbody>
</table>
<p class="invoice-payment" translate translate-values="{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT'), AMOUNT:(30.0 | currency)}">
{{ 'app.admin.invoices.settlement_by_debit_card_on_DATE_at_TIME_for_an_amount_of_AMOUNT' }}
</p>
</div>
<div medium-editor class="invoice-text invoice-editable" ng-model="invoice.text.content"
options='{
"placeholder": "{{ "app.admin.invoices.important_notes" | translate }}",
"buttons": ["underline"]
}'
ng-blur="textEditEnd($event)">
</div>
<div medium-editor class="invoice-legals invoice-editable" ng-model="invoice.legals.content"
options='{
"placeholder": "{{ "app.admin.invoices.address_and_legal_information" | translate }}",
"buttons": ["bold", "underline"]
}'
ng-blur="legalsEditEnd($event)">
</div>
</form>
<div class="invoice-file">
<h3 class="m-l" translate>{{ 'app.admin.invoices.filename' }}</h3>
<i class="fa fa-file-pdf-o" aria-hidden="true"></i>
<span class="filename"><span class="prefix" ng-click="openEditPrefix()">{{file.prefix}}</span>-{{file.nextId}}_{{file.date}}.pdf</span>
</div>
<script type="text/ng-template" id="editReference.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'app.admin.invoices.invoice_reference' }}</h3>
</div>
<div class="modal-body row">
<div class="elements col-md-4">
<h4>Éléments</h4>
<ul>
<li ng-click="invoice.reference.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
<li ng-click="invoice.reference.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
<li ng-click="invoice.reference.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
<li ng-click="invoice.reference.help = 'addInvoiceNumber.html'">{{ 'app.admin.invoices.num_of_invoice' | translate }}</li>
<li ng-click="invoice.reference.help = 'addOnlineInfo.html'">{{ 'app.admin.invoices.online_sales' | translate }}</li>
<%# <li ng-click="invoice.reference.help = 'addWalletInfo.html'">{{ 'app.admin.invoices.wallet' | translate }}</li> %>
<li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'app.admin.invoices.refund' | translate }}</li>
</ul>
</div>
<div class="col-md-8">
<div class="model">
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
<input type="text" class="form-control" ng-model="model">
</div>
<div class="help">
<h4 translate>{{ 'app.admin.invoices.documentation' }}</h4>
<ng-include src="invoice.reference.help" autoscroll="true">
</ng-include>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
</div>
</script>
<script type="text/ng-template" id="addYear.html">
<table class="invoice-element-legend">
<tr><td><strong>YY</strong></td><td translate>{{ 'app.admin.invoices.2_digits_year' }}</td></tr>
<tr><td><strong>YYYY</strong></td><td translate>{{ 'app.admin.invoices.4_digits_year' }}</td></tr>
</table>
</script>
<script type="text/ng-template" id="addMonth.html">
<table class="invoice-element-legend">
<tr><td><strong>M</strong></td><td translate>{{ 'app.admin.invoices.month_number' }}</td></tr>
<tr><td><strong>MM</strong></td><td translate>{{ 'app.admin.invoices.2_digits_month_number' }}</td></tr>
<tr><td><strong>MMM</strong></td><td translate>{{ 'app.admin.invoices.3_characters_month_name' }}</td></tr>
</table>
</script>
<script type="text/ng-template" id="addDay.html">
<table class="invoice-element-legend">
<tr><td><strong>D</strong></td><td translate>{{ 'app.admin.invoices.day_in_the_month' }}</td></tr>
<tr><td><strong>DD</strong></td><td translate>{{ 'app.admin.invoices.2_digits_day_in_the_month' }}</td></tr>
</table>
</script>
<script type="text/ng-template" id="addInvoiceNumber.html">
<table class="invoice-element-legend">
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'app.admin.invoices.n_digits_daily_count_of_invoices' }}</td></tr>
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'app.admin.invoices.n_digits_monthly_count_of_invoices' }}</td></tr>
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'app.admin.invoices.n_digits_annual_amount_of_invoices' }}</td></tr>
</table>
<span class="bottom-notes" translate>{{ 'app.admin.invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
</script>
<script type="text/ng-template" id="addOrderNumber.html">
<table class="invoice-element-legend">
<tr><td><strong>nn...nn</strong></td><td translate>{{ 'app.admin.invoices.n_digits_count_of_orders' }}</td></tr>
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'app.admin.invoices.n_digits_daily_count_of_orders' }}</td></tr>
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'app.admin.invoices.n_digits_monthly_count_of_orders' }}</td></tr>
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'app.admin.invoices.n_digits_annual_amount_of_orders' }}</td></tr>
</table>
<span class="bottom-notes" translate>{{ 'app.admin.invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
</script>
<script type="text/ng-template" id="addOnlineInfo.html">
<table class="invoice-element-legend">
<tr><td><strong>X[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_online_sales_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe' | translate }}</td></tr>
</table>
</script>
<script type="text/ng-template" id="addWalletInfo.html">
<table class="invoice-element-legend">
<tr><td><strong>W[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_WPM_will_add_PM_to_the_invoices_settled_with_wallet' | translate }}</td></tr>
</table>
</script>
<script type="text/ng-template" id="addRefundInfo.html">
<table class="invoice-element-legend">
<tr><td><strong>R[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned' | translate }}<mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_an_online_sales_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_RA_will_add_A_to_the_refund_invoices' | translate }}</td></tr>
</table>
</script>
<script type="text/ng-template" id="editCode.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'app.admin.invoices.code' }}</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label for="enableCode" class="control-label" translate>{{ 'app.admin.invoices.enable_the_code' }}</label>
<input bs-switch
ng-model="isSelected"
id="enableCode"
type="checkbox"
class="form-control m-l-sm"
switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
switch-animate="true"/>
</div>
<div class="form-group" ng-show="isSelected">
<label for="codeModel" class="control-label" translate>{{ 'app.admin.invoices.code' }}</label>
<input id="codeModel" type="text" ng-model="codeModel" class="form-control"/>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
</div>
</script>
<script type="text/ng-template" id="editNumber.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'app.admin.invoices.order_number' }}</h3>
</div>
<div class="modal-body row">
<div class="elements col-md-4">
<h4 translate>{{ 'app.admin.invoices.elements' }}</h4>
<ul>
<li ng-click="invoice.number.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
<li ng-click="invoice.number.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
<li ng-click="invoice.number.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
<li ng-click="invoice.number.help = 'addOrderNumber.html'">{{ 'app.admin.invoices.order_num' | translate }}</li>
</ul>
</div>
<div class="col-md-8">
<div class="model">
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
<input type="text" class="form-control" ng-model="model">
</div>
<div class="help">
<h4 translate>{{ 'app.admin.invoices.documentation' }}</h4>
<ng-include src="invoice.number.help" autoscroll="true">
</ng-include>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
</div>
</script>
<script type="text/ng-template" id="editVAT.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'app.admin.invoices.VAT' }}</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label for="enableVAT" class="control-label" translate>{{ 'app.admin.invoices.enable_VAT' }}</label>
<input bs-switch
ng-model="isSelected"
id="enableVAT"
type="checkbox"
class="form-control m-l-sm"
switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
switch-animate="true"/>
</div>
<div class="form-group" ng-show="isSelected">
<label for="vatRate" class="control-label" translate>{{ 'app.admin.invoices.VAT_rate' }}</label>
<div class="input-group">
<span class="input-group-addon">% </span>
<input id="vatRate" type="number" ng-model="rate" class="form-control" min="0" max="100"/>
</div>
</div>
<div class="m-t-lg">
<h4 translate>{{ 'app.admin.invoices.VAT_history' }}</h4>
<table class="table scrollable-3-cols">
<thead>
<tr>
<th translate>{{ 'app.admin.invoices.VAT_rate' }}</th>
<th translate>{{ 'app.admin.invoices.changed_at' }}</th>
<th translate>{{ 'app.admin.invoices.changed_by' }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="value in history | orderBy:'-date'">
<td>
<span class="no-user-label" ng-show="value.enabled === false" translate>{{'app.admin.invoices.VAT_disabled'}}</span>
<span class="no-user-label" ng-show="value.enabled === true" translate>{{'app.admin.invoices.VAT_enabled'}}</span>
<span ng-show="value.rate">{{value.rate}}</span>
</td>
<td>{{value.date | amDateFormat:'L LT'}}</td>
<td>{{value.user.name}}<span class="no-user-label" ng-hide="value.user" translate>{{ 'app.admin.invoices.deleted_user' }}</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
</div>
</script>
<script type="text/ng-template" id="editPrefix.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'app.admin.invoices.filename' }}</h3>
</div>
<div class="modal-body">
<p class="alert alert-info m-h-md" translate>
{{ 'app.admin.invoices.prefix_info' }}
</p>
<div>
<div class="model">
<label for="prefix" translate>{{ 'app.admin.invoices.prefix' }}</label>
<input type="text" id="prefix" class="form-control" ng-model="model">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
</div>
</script>

View File

@ -1,35 +0,0 @@
<form class="{{classes}}" name="settingSelectMultipleForm">
<div class="form-group">
<label for="setting-{{setting.name}}" class="control-label m-r" translate>{{ label }}</label>
<select class="form-control"
id="setting-{{setting.name}}"
ng-model="selection"
ng-required="required"
ng-options="opt for opt in options"
multiple>
</select>
</div>
<div>
<button ng-click="removeItem()" class="btn btn-default"><i class="fa fa-trash"></i></button>
<button ng-click="addItem()" class="btn btn-default"><i class="fa fa-plus"></i></button>
</div>
<button name="button" class="btn btn-warning m-t" ng-click="save(setting)" ng-disabled="settingSelectMultipleForm.$invalid" translate>{{ 'app.shared.buttons.save' }}</button>
</form>
<script type="text/ng-template" id="newSelectOption.html">
<div>
<div class="modal-header">
<h3 class="modal-title" translate>{{ titleNew }}</h3>
</div>
<div class="modal-body">
<p class="alert alert-info" ng-show="descriptionNew" ng-bind-html="descriptionNew | translate"></p>
<form class="row m-md" name="newSelectOptionForm">
<input type="text" class="form-control" ng-model="value" required>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-disabled="newSelectOptionForm.$invalid" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="dismiss()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
</div>
</script>

View File

@ -1,18 +0,0 @@
<div>
<section class="heading">
<div class="row no-gutter">
<ng-include src="'<%= asset_path "dashboard/nav.html" %>'"></ng-include>
</div>
</section>
<div class="row no-gutter">
<div class="col-md-12 m m-t-lg">
<ng-include src="'<%= asset_path "wallet/show.html" %>'"></ng-include>
</div>
<div class="col-md-12 m m-t-lg">
<ng-include src="'<%= asset_path "wallet/transactions.html" %>'"></ng-include>
</div>
</div>
</div>

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 619 B

After

Width:  |  Height:  |  Size: 619 B

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1015 B

After

Width:  |  Height:  |  Size: 1015 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,87 @@
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import 'jquery';
import {} from 'jquery-ujs';
import 'bootstrap-sass';
import '../src/javascript/lib/polyfill';
import 'angular';
import 'angular-i18n/angular-locale_<%= Rails.application.secrets.angular_locale %>.js';
import 'angular-cookies';
import 'angular-resource';
import 'angular-sanitize';
import 'angular-touch';
import '@uirouter/angularjs';
import 'angular-ui-bootstrap';
import 'ui-select';
import 'moment';
import 'moment/locale/<%= Rails.application.secrets.moment_locale %>.js';
import 'moment-timezone';
import 'angular-ui-calendar';
import 'fullcalendar';
import 'fullcalendar/dist/locale/<%= Rails.application.secrets.fullcalendar_locale %>.js';
import 'angular-moment';
import 'ngUpload';
import 'jasny-bootstrap/js/fileinput';
import 'holderjs';
import 'AngularDevise';
import '../src/javascript/lib/devise-modal';
import 'angular-growl-v2';
import 'angular-xeditable';
import 'checklist-model/checklist-model';
import 'angular-unsavedchanges/lib/unsavedChanges';
import 'angular-loading-bar/src/loading-bar';
import 'angular-scroll/angular-scroll';
import 'angular-google-analytics/dist/angular-google-analytics';
import '../src/javascript/lib/dirDisqus';
import '../src/javascript/lib/humanize';
import 'underscore/underscore';
import 'elasticsearch-browser/elasticsearch.angular';
import 'd3/d3';
import 'nvd3/build/nv.d3.js';
import 'twitter-fetcher';
import 'medium-editor/dist/js/medium-editor';
import 'angular-medium-editor/dist/angular-medium-editor';
import 'bootstrap-switch/dist/js/bootstrap-switch';
import 'angular-bootstrap-switch/dist/angular-bootstrap-switch';
import 'angular-base64-upload/dist/angular-base64-upload.min';
import 'summernote';
import 'summernote/lang/summernote-<%= Rails.application.secrets.summernote_locale %>.js';
import 'angular-summernote/dist/angular-summernote';
import '../src/javascript/lib/summernote-ext-nugget';
import '@claviska/jquery-minicolors/jquery.minicolors.js';
import 'angular-minicolors/angular-minicolors.js';
import 'angular-translate/dist/angular-translate';
import 'angular-translate-loader-partial/angular-translate-loader-partial';
import 'messageformat/messageformat';
import 'angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat';
import 'ng-fittext/dist/ng-FitText.min';
import 'angular-aside/dist/js/angular-aside';
import 'ng-caps-lock/ng-caps-lock';
import 'angular-recaptcha';
import 'codemirror/lib/codemirror';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/mode/css/css';
import 'codemirror/mode/sass/sass';
import 'angular-ui-codemirror/src/ui-codemirror';
import 'angular-hotkeys/build/hotkeys';
import 'hone/dist/hone';
import 'tether/dist/js/tether';
import 'angular-bind-html-compile/angular-bind-html-compile';
import 'angular-ui-tour/app/angular-ui-tour';
import '@fortawesome/fontawesome-free';
import '@fortawesome/fontawesome-free/js/v4-shims';
require('../src/javascript/app.js');
require('../src/javascript/router.js');
require('../src/javascript/plugins.js.erb');
function importAll (r) { r.keys().forEach(r); }
importAll(require.context('../src/javascript/controllers/', true, /.*/));
importAll(require.context('../src/javascript/services/', true, /.*/));
importAll(require.context('../src/javascript/directives/', true, /.*/));
importAll(require.context('../src/javascript/filters/', true, /.*/));
importAll(require.context('../images', true));
importAll(require.context('../templates', true));

View File

@ -0,0 +1,18 @@
@import '~ui-select/dist/select';
@import '~fullcalendar/dist/fullcalendar';
@import '~jasny-bootstrap/dist/css/jasny-bootstrap';
@import '~angular-growl-v2/build/angular-growl';
@import '~angular-xeditable/dist/css/xeditable';
@import '~angular-loading-bar/build/loading-bar';
@import '~nvd3/build/nv.d3';
@import '~@fortawesome/fontawesome-free/css/all';
@import '~@fortawesome/fontawesome-free/css/v4-shims';
@import '~medium-editor/dist/css/medium-editor';
@import '~medium-editor/dist/css/themes/default';
@import '~bootstrap-switch/dist/css/bootstrap3/bootstrap-switch';
@import '~summernote/src/styles/summernote-bs3';
@import '~@claviska/jquery-minicolors/jquery.minicolors';
@import '~angular-aside/dist/css/angular-aside';
@import '~codemirror/lib/codemirror';
@import '../src/stylesheets/application.scss';

View File

@ -0,0 +1,4 @@
<% PluginRegistry.stylesheets.each do |stylesheet| %>
<% basename = File.basename(stylesheet,'.scss') %>
<%= "@import '#{basename}';" %>
<% end %>

View File

@ -0,0 +1,2 @@
@import '../src/stylesheets/app.printer';

View File

@ -8,7 +8,6 @@
// eslint-disable-next-line no-use-before-define
var Application = Application || {};
Application.Constants = angular.module('application.constants', []);
Application.Services = angular.module('application.services', []);
Application.Controllers = angular.module('application.controllers', []);
Application.Filters = angular.module('application.filters', []);
@ -16,7 +15,7 @@ Application.Directives = angular.module('application.directives', []);
angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.router', 'ui.bootstrap',
'ngUpload', 'duScroll', 'application.filters', 'application.services', 'application.directives',
'frapontillo.bootstrap-switch', 'application.constants', 'application.controllers', 'application.router',
'frapontillo.bootstrap-switch', 'application.controllers', 'application.router',
'ui.select', 'ui.calendar', 'angularMoment', 'Devise', 'angular-growl', 'xeditable',
'checklist-model', 'unsavedChanges', 'angular-loading-bar', 'ngTouch', 'angular-google-analytics',
'angularUtils.directives.dirDisqus', 'summernote', 'elasticsearch', 'angular-medium-editor', 'naif.base64',
@ -66,8 +65,8 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
$translateProvider.preferredLanguage(Fablab.locale);
// End the tour when the user clicks the forward or back buttons of the browser
TourConfigProvider.enableNavigationInterceptors();
}]).run(['$rootScope', '$log', 'AuthService', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics',
function ($rootScope, $log, AuthService, Auth, amMoment, $state, editableOptions, Analytics) {
}]).run(['$rootScope', '$log', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics',
function ($rootScope, $log, Auth, amMoment, $state, editableOptions, Analytics) {
// Angular-moment (date-time manipulations library)
amMoment.changeLocale(Fablab.moment_locale);
@ -141,8 +140,10 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
};
}]).constant('angularMomentConfig', {
timezone: Fablab.timezone
});
}).constant('moment', require('moment-timezone'));
angular.isUndefinedOrNull = function (val) {
return angular.isUndefined(val) || val === null;
};
module.exports = { Application };

View File

@ -87,7 +87,7 @@ class AuthenticationController {
*/
$scope.defineDataMapping = function (mapping) {
$uibModal.open({
templateUrl: '<%= asset_path "admin/authentications/_data_mapping.html" %>',
templateUrl: '/admin/authentications/_data_mapping.html',
size: 'md',
resolve: {
field () { return mapping; },

View File

@ -46,18 +46,17 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
$scope.eventsInCalendar = (settingsPromise.events_in_calendar === 'true');
// bind the availabilities slots with full-Calendar events
$scope.eventSources = [];
$scope.eventSources.push({
$scope.eventSources = [{
url: '/api/availabilities',
textColor: 'black'
});
}];
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
slotDuration: BASE_SLOT,
snapDuration: BOOKING_SNAP,
selectable: true,
selecHelper: true,
selectHelper: true,
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss')),
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss')),
select (start, end, jsEvent, view) {
@ -69,7 +68,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
eventRender (event, element, view) {
return eventRenderCb(event, element, view);
},
viewRender(view, element) {
viewRender (view, element) {
return viewRenderCb(view, element);
},
loading (isLoading, view) {
@ -101,7 +100,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
{ id: slot.slot_id },
function (data, status) { // success
// update the canceled_at attribute
for (let resa of Array.from($scope.reservations)) {
for (const resa of Array.from($scope.reservations)) {
if (resa.slot_id === data.id) {
resa.canceled_at = data.canceled_at;
break;
@ -185,9 +184,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
},
function () {
// the admin has confirmed, remove the plan
const plans = _.drop($scope.availability.plan_ids, plan.id);
_.drop($scope.availability.plan_ids, plan.id);
return Availability.update({ id: $scope.availability.id }, { availability: { plans_attributes: [{ id: plan.id, _destroy: true }] } }
Availability.update({ id: $scope.availability.id }, { availability: { plans_attributes: [{ id: plan.id, _destroy: true }] } }
, function (data, status) { // success
// update the plan_ids attribute
$scope.availability.plan_ids = data.plan_ids;
@ -273,7 +272,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
// open a confirmation dialog
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "admin/calendar/deleteRecurrent.html" %>',
templateUrl: '/admin/calendar/deleteRecurrent.html',
size: 'md',
controller: 'DeleteRecurrentAvailabilityController',
resolve: {
@ -282,7 +281,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
});
// once the dialog was closed, do things depending on the result
modalInstance.result.then(function (res) {
if (res.status == 'success') {
if (res.status === 'success') {
$scope.availability = null;
}
for (const availability of res.availabilities) {
@ -355,7 +354,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('calendar') < 0) {
uitour.start();
}
}
};
/* PRIVATE SCOPE */
@ -380,12 +379,12 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
*
* @returns {array}
*/
var availabilityPlans = function() {
const availabilityPlans = function () {
const plansClassifiedByGroup = [];
const _plans = _.filter(plansPromise, function (p) { return _.include($scope.availability.plan_ids, p.id) });
for (let group of Array.from(groupsPromise)) {
const _plans = _.filter(plansPromise, function (p) { return _.includes($scope.availability.plan_ids, p.id); });
for (const group of Array.from(groupsPromise)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (let plan of Array.from(_plans)) {
for (const plan of Array.from(_plans)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
@ -419,14 +418,14 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
// then we open a modal window to let the admin specify the slot type
const modalInstance = $uibModal.open({
templateUrl: '<%= asset_path "admin/calendar/eventModal.html" %>',
templateUrl: '/admin/calendar/eventModal.html',
controller: 'CreateEventModalController',
backdrop: 'static',
keyboard: false,
resolve: {
start() { return start; },
end() { return end; },
slots() { return Math.ceil(slots); },
start () { return start; },
end () { return end; },
slots () { return Math.ceil(slots); },
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }],
@ -434,7 +433,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
plansPromise: ['Plan', function (Plan) { return Plan.query().$promise; }],
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }],
slotDurationPromise: ['Setting', function (Setting) { return Setting.get({ name: 'slot_duration' }).$promise; }]
} });
}
});
// when the modal is closed, we send the slot to the server for saving
modalInstance.result.then(
function (availability) {
@ -472,10 +472,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
$scope.availability.plans = availabilityPlans();
if ($scope.availabilityDom) {
$scope.availabilityDom.classList.remove("fc-selected")
$scope.availabilityDom.classList.remove('fc-selected');
}
$scope.availabilityDom = jsEvent.target.closest('.fc-event');
$scope.availabilityDom.classList.add("fc-selected")
$scope.availabilityDom.classList.add('fc-selected');
// if the user has clicked on the delete event button, delete the event
if ($(jsEvent.target).hasClass('remove-event')) {
@ -495,14 +495,13 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
if (event.available_type !== 'event') {
element.find('.fc-content').prepend('<span class="remove-event">x&nbsp;</span>');
}
if (event.tags.length > 0) {
if (event.tags && event.tags.length > 0) {
let html = '';
for (let tag of Array.from(event.tags)) {
for (const tag of Array.from(event.tags)) {
html += `<span class='label label-success text-white'>${tag.name}</span> `;
}
element.find('.fc-title').append(`<br/>${html}`);
}
// force return to prevent coffee-script auto-return to return random value (possiblity falsy)
};
/**
@ -510,10 +509,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* @see https://fullcalendar.io/docs/resource_data/loading/
*/
const loadingCb = function (isLoading, view) {
if (isLoading) {
if (isLoading && uiCalendarConfig.calendars.calendar) {
// we remove existing events when fetching starts to prevent duplicates
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
}
};
@ -521,7 +519,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* Triggered when the view is changed
* @see https://fullcalendar.io/docs/v3/viewRender#v2
*/
const viewRenderCb = function(view, element) {
const viewRenderCb = function (view, element) {
// we unselect the current event to keep consistency
$scope.availability = null;
$scope.availabilityDom = null;
@ -634,7 +632,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
/**
* Select/unselect all the machines
*/
$scope.toggleAll = function() {
$scope.toggleAll = function () {
const count = $scope.selectedMachines.length;
$scope.selectedMachines = [];
$scope.selectedMachinesBinding = {};
@ -642,9 +640,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.machines.forEach(function (machine) {
$scope.selectedMachines.push(machine);
$scope.selectedMachinesBinding[machine.id] = true;
})
});
}
}
};
/**
* Adds or removes the provided plan from the current slot
@ -662,7 +660,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
/**
* Select/unselect all the plans
*/
$scope.toggleAllPlans = function() {
$scope.toggleAllPlans = function () {
const count = $scope.selectedPlans.length;
$scope.selectedPlans = [];
$scope.selectedPlansBinding = {};
@ -670,7 +668,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
plansPromise.forEach(function (plan) {
$scope.selectedPlans.push(plan);
$scope.selectedPlansBinding[plan.id] = true;
})
});
}
};
@ -739,7 +737,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
*/
$scope.isTypeDivided = function () {
return isTypeDivided($scope.availability.available_type);
}
};
/* PRIVATE SCOPE */
@ -755,7 +753,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
}
// when disable is only subscriptions option, reset all selected plans
$scope.$watch('isOnlySubscriptions', function(value) {
$scope.$watch('isOnlySubscriptions', function (value) {
if (!value) {
$scope.selectedPlans = [];
$scope.selectedPlansBinding = {};
@ -763,14 +761,14 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
});
// group plans by Group
for (let group of groupsPromise.filter(g => !g.disabled)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (let plan of plansPromise.filter(g => !g.disabled)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
$scope.plansClassifiedByGroup.push(groupObj);
}
for (const group of groupsPromise.filter(g => !g.disabled)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (const plan of plansPromise.filter(g => !g.disabled)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
$scope.plansClassifiedByGroup.push(groupObj);
}
}
// When the slot duration changes, we increment the availability to match the value
@ -784,9 +782,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
// When the number of slot changes, we increment the availability to match the value
$scope.$watch('slots_nb', function (newValue, oldValue, scope) {
start = moment($scope.start);
start.add($scope.availability.slot_duration * newValue, 'minutes');
$scope.end = start.toDate();
start = moment($scope.start);
start.add($scope.availability.slot_duration * newValue, 'minutes');
$scope.end = start.toDate();
});
// When we configure a machine/space availability, do not let the user change the end time, as the total
@ -803,7 +801,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.end = moment($scope.start).add(upper, 'minutes').toDate();
$scope.slots_nb = upperSlots;
} else {
$scope.slots_nb = slotsCurrentRange;
$scope.slots_nb = slotsCurrentRange;
}
$scope.availability.end_at = $scope.end;
} else {
@ -844,8 +842,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
* Test if the provided availability type is divided in slots
*/
const isTypeDivided = function (type) {
return ((type === 'machines') || (type === 'space'));
}
return ((type === 'machines') || (type === 'space'));
};
/**
* Validates that a machine or more was/were selected before continuing to step 3 (adjust time + tags)
@ -891,7 +889,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.availability.slot_duration = parseInt(slotDurationPromise.setting.value, 10);
}
$scope.step++;
}
};
/**
* Compute the various occurrences of the availability, according to the recurrence settings
@ -923,7 +921,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.reservableName = '';
switch ($scope.availability.available_type) {
case 'machines':
$scope.reservableName = localizedList($scope.selectedMachines)
$scope.reservableName = localizedList($scope.selectedMachines);
break;
case 'training':
$scope.reservableName = `<strong>${$scope.selectedTraining.name}</strong>`;
@ -932,25 +930,25 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.reservableName = `<strong>${$scope.selectedSpace.name}</strong>`;
break;
default:
$scope.reservableName = `<span class="warning">${_t("app.admin.calendar.none")}</span>`;
$scope.reservableName = `<span class="warning">${_t('app.admin.calendar.none')}</span>`;
}
const tags = $scope.tags.filter(function (t) {
return $scope.availability.tag_ids.indexOf(t.id) > -1;
})
});
$scope.tagsName = localizedList(tags);
if ($scope.isOnlySubscriptions && $scope.selectedPlans.length > 0) {
$scope.plansName = localizedList($scope.selectedPlans);
}
}
};
const localizedList = function (items) {
if (items.length === 0) return `<span class="text-gray text-italic">${_t("app.admin.calendar.none")}</span>`;
if (items.length === 0) return `<span class="text-gray text-italic">${_t('app.admin.calendar.none')}</span>`;
const names = items.map(function (i) { return $sce.trustAsHtml(`<strong>${i.name}</strong>`); });
if (items.length > 1) return names.slice(0, -1).join(', ') + ` ${_t('app.admin.calendar.and')} ` + names[names.length - 1];
return names[0];
}
};
// !!! MUST BE CALLED AT THE END of the controller
return initialize();
@ -962,7 +960,6 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
*/
Application.Controllers.controller('DeleteRecurrentAvailabilityController', ['$scope', '$uibModalInstance', 'Availability', 'availabilityPromise', 'growl', '_t',
function ($scope, $uibModalInstance, Availability, availabilityPromise, growl, _t) {
// is the current slot (to be deleted) recurrent?
$scope.isRecurrent = availabilityPromise.is_recurrent;
@ -982,17 +979,17 @@ Application.Controllers.controller('DeleteRecurrentAvailabilityController', ['$s
if (res.deleted > 1) {
growl.success(_t(
'app.admin.calendar.slots_deleted',
{START: moment(start_at).format('LL LT'), COUNT: res.deleted - 1}
{ START: moment(start_at).format('LL LT'), COUNT: res.deleted - 1 }
));
} else {
growl.success(_t(
'app.admin.calendar.slot_successfully_deleted',
{START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT')}
{ START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT') }
));
}
$uibModalInstance.close({
status: 'success',
availabilities: res.details.map(function (d) { return d.availability.id })
availabilities: res.details.map(function (d) { return d.availability.id; })
});
},
function (res) {
@ -1001,32 +998,30 @@ Application.Controllers.controller('DeleteRecurrentAvailabilityController', ['$s
if (data.total > 1) {
growl.warning(_t(
'app.admin.calendar.slots_not_deleted',
{TOTAL: data.total, COUNT: data.total - data.deleted}
{ TOTAL: data.total, COUNT: data.total - data.deleted }
));
} else {
growl.error(_t(
'app.admin.calendar.unable_to_delete_the_slot',
{START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT')}
{ START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT') }
));
}
$uibModalInstance.close({
status: 'failed',
availabilities: data.details.filter(function (d) { return d.status }).map(function (d) { return d.availability.id })
availabilities: data.details.filter(function (d) { return d.status; }).map(function (d) { return d.availability.id; })
});
});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);
/**
* Controller used in the iCalendar (ICS) imports management page
*/
@ -1061,8 +1056,8 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
// failed
growl.error(_t('app.admin.icalendar.create_error'));
console.error(error);
})
}
});
};
/**
* Return a CSS-like style of the given calendar configuration
@ -1071,11 +1066,11 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
$scope.calendarStyle = function (calendar) {
return {
'border-color': calendar.color,
'color': calendar.text_color,
'width': calendar.text_hidden ? '50px' : 'auto',
'height': calendar.text_hidden ? '21px' : 'auto'
color: calendar.text_color,
width: calendar.text_hidden ? '50px' : 'auto',
height: calendar.text_hidden ? '21px' : 'auto'
};
}
};
/**
* Delete the given calendar from the database
@ -1108,8 +1103,8 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
}
);
}
)
}
);
};
/**
* Asynchronously re-fetches the events from the given calendar
@ -1126,7 +1121,7 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
growl.error(_t('app.admin.icalendar.sync_failed'));
console.error(error);
}
)
}
);
};
}
]);

View File

@ -279,7 +279,7 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
*/
$scope.newPriceCategory = function () {
$uibModal.open({
templateUrl: '<%= asset_path "admin/events/price_form.html" %>',
templateUrl: '/admin/events/price_form.html',
size: 'md',
resolve: {
category () { return {}; }
@ -307,7 +307,7 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
return growl.error(_t('app.admin.events.unexpected_error_occurred_please_refresh'));
} else {
return $uibModal.open({
templateUrl: '<%= asset_path "admin/events/price_form.html" %>',
templateUrl: '/admin/events/price_form.html',
size: 'md',
resolve: {
category () { return $scope.priceCategories[index]; }
@ -645,7 +645,7 @@ Application.Controllers.controller('EditEventController', ['$scope', '$state', '
// open a choice edit-mode dialog
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "events/editRecurrent.html" %>',
templateUrl: '/events/editRecurrent.html',
size: 'md',
controller: 'EditRecurrentEventController',
resolve: {

View File

@ -58,8 +58,8 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
prefix: settings.invoice_prefix,
nextId: 40,
date: moment().format('DDMMYYYY'),
templateUrl: 'editPrefix.html'
}
templateUrl: '/admin/invoices/settings/editPrefix.html'
};
// Invoices parameters
$scope.invoice = {
@ -67,22 +67,22 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
reference: {
model: '',
help: null,
templateUrl: 'editReference.html'
templateUrl: '/admin/invoices/settings/editReference.html'
},
code: {
model: '',
active: true,
templateUrl: 'editCode.html'
templateUrl: '/admin/invoices/settings/editCode.html'
},
number: {
model: '',
help: null,
templateUrl: 'editNumber.html'
templateUrl: '/admin/invoices/settings/editNumber.html'
},
VAT: {
rate: 19.6,
active: false,
templateUrl: 'editVAT.html'
templateUrl: '/admin/invoices/settings/editVAT.html'
},
text: {
content: ''
@ -96,87 +96,87 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
$scope.settings = {
journalCode: {
name: 'accounting_journal_code',
value: settings['accounting_journal_code']
value: settings.accounting_journal_code
},
cardClientCode: {
name: 'accounting_card_client_code',
value: settings['accounting_card_client_code']
value: settings.accounting_card_client_code
},
cardClientLabel: {
name: 'accounting_card_client_label',
value: settings['accounting_card_client_label']
value: settings.accounting_card_client_label
},
walletClientCode: {
name: 'accounting_wallet_client_code',
value: settings['accounting_wallet_client_code']
value: settings.accounting_wallet_client_code
},
walletClientLabel: {
name: 'accounting_wallet_client_label',
value: settings['accounting_wallet_client_label']
value: settings.accounting_wallet_client_label
},
otherClientCode: {
name: 'accounting_other_client_code',
value: settings['accounting_other_client_code']
value: settings.accounting_other_client_code
},
otherClientLabel: {
name: 'accounting_other_client_label',
value: settings['accounting_other_client_label']
value: settings.accounting_other_client_label
},
walletCode: {
name: 'accounting_wallet_code',
value: settings['accounting_wallet_code']
value: settings.accounting_wallet_code
},
walletLabel: {
name: 'accounting_wallet_label',
value: settings['accounting_wallet_label']
value: settings.accounting_wallet_label
},
vatCode: {
name: 'accounting_VAT_code',
value: settings['accounting_VAT_code']
value: settings.accounting_VAT_code
},
vatLabel: {
name: 'accounting_VAT_label',
value: settings['accounting_VAT_label']
value: settings.accounting_VAT_label
},
subscriptionCode: {
name: 'accounting_subscription_code',
value: settings['accounting_subscription_code']
value: settings.accounting_subscription_code
},
subscriptionLabel: {
name: 'accounting_subscription_label',
value: settings['accounting_subscription_label']
value: settings.accounting_subscription_label
},
machineCode: {
name: 'accounting_Machine_code',
value: settings['accounting_Machine_code']
value: settings.accounting_Machine_code
},
machineLabel: {
name: 'accounting_Machine_label',
value: settings['accounting_Machine_label']
value: settings.accounting_Machine_label
},
trainingCode: {
name: 'accounting_Training_code',
value: settings['accounting_Training_code']
value: settings.accounting_Training_code
},
trainingLabel: {
name: 'accounting_Training_label',
value: settings['accounting_Training_label']
value: settings.accounting_Training_label
},
eventCode: {
name: 'accounting_Event_code',
value: settings['accounting_Event_code']
value: settings.accounting_Event_code
},
eventLabel: {
name: 'accounting_Event_label',
value: settings['accounting_Event_label']
value: settings.accounting_Event_label
},
spaceCode: {
name: 'accounting_Space_code',
value: settings['accounting_Space_code']
value: settings.accounting_Space_code
},
spaceLabel: {
name: 'accounting_Space_label',
value: settings['accounting_Space_label']
value: settings.accounting_Space_label
}
};
@ -220,12 +220,12 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
$scope.generateAvoirForInvoice = function (invoice) {
// open modal
const modalInstance = $uibModal.open({
templateUrl: '<%= asset_path "admin/invoices/avoirModal.html" %>',
templateUrl: '/admin/invoices/avoirModal.html',
controller: 'AvoirModalController',
resolve: {
invoice () { return invoice; },
closedPeriods() { return AccountingPeriod.query().$promise; },
lastClosingEnd() { return AccountingPeriod.lastClosingEnd().$promise; }
closedPeriods () { return AccountingPeriod.query().$promise; },
lastClosingEnd () { return AccountingPeriod.lastClosingEnd().$promise; }
}
});
@ -311,10 +311,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
growl.success(_t('app.admin.invoices.invoice_reference_successfully_saved'));
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_invoice_reference'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_invoice_reference'));
console.error(error);
});
});
};
@ -352,10 +352,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_invoicing_code'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_invoicing_code'));
console.error(error);
});
return Setting.update({ name: 'invoice_code-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
@ -367,10 +367,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_invoicing_code'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_invoicing_code'));
console.error(error);
});
});
};
@ -401,10 +401,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return growl.success(_t('app.admin.invoices.order_number_successfully_saved'));
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_order_number'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_order_number'));
console.error(error);
});
});
};
@ -440,15 +440,14 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
$scope.ok = function () { $uibModalInstance.close({ rate: $scope.rate, active: $scope.isSelected }); };
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
const initialize = function() {
const initialize = function () {
rateHistory.setting.history.forEach(function (rate) {
$scope.history.push({ date: rate.created_at, rate: rate.value, user: rate.user })
$scope.history.push({ date: rate.created_at, rate: rate.value, user: rate.user });
});
activeHistory.setting.history.forEach(function (v) {
$scope.history.push({ date: v.created_at, enabled: v.value === 'true', user: v.user })
$scope.history.push({ date: v.created_at, enabled: v.value === 'true', user: v.user });
});
}
};
initialize();
}]
@ -462,10 +461,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_VAT_rate'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_VAT_rate'));
console.error(error);
});
return Setting.update({ name: 'invoice_VAT-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
@ -477,10 +476,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_VAT'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_VAT'));
console.error(error);
});
});
};
@ -494,7 +493,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
templateUrl: $scope.file.templateUrl,
size: 'lg',
resolve: {
model () { return $scope.file.prefix;}
model () { return $scope.file.prefix; }
},
controller: ['$scope', '$uibModalInstance', 'model', function ($scope, $uibModalInstance, model) {
$scope.model = model;
@ -505,15 +504,15 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return modalInstance.result.then(function (model) {
Setting.update({ name: 'invoice_prefix' }, { value: model }, function (data) {
$scope.file.prefix = model;
return growl.success(_t('app.admin.invoices.prefix_successfully_saved'));
}
, function (error) {
if (error.status === 304) return;
$scope.file.prefix = model;
return growl.success(_t('app.admin.invoices.prefix_successfully_saved'));
}
, function (error) {
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_prefix'));
console.error(error);
});
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_prefix'));
console.error(error);
});
});
};
@ -527,10 +526,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return growl.success(_t('app.admin.invoices.text_successfully_saved'));
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_text'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_text'));
console.error(error);
});
};
@ -544,10 +543,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return growl.success(_t('app.admin.invoices.address_and_legal_information_successfully_saved'));
}
, function (error) {
if (error.status === 304) return;
if (error.status === 304) return;
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_address_and_the_legal_information'));
console.error(error);
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_address_and_the_legal_information'));
console.error(error);
});
};
@ -557,7 +556,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
*/
$scope.handleFilterChange = function () {
if (searchTimeout) clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
searchTimeout = setTimeout(function () {
resetSearchInvoice();
invoiceSearch();
}, 300);
@ -576,47 +575,47 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
* Open a modal allowing the user to close an accounting period and to
* view all periods already closed.
*/
$scope.closeAnAccountingPeriod = function() {
$scope.closeAnAccountingPeriod = function () {
// open modal
$uibModal.open({
templateUrl: '<%= asset_path "admin/invoices/closePeriodModal.html" %>',
templateUrl: '/admin/invoices/closePeriodModal.html',
controller: 'ClosePeriodModalController',
backdrop: 'static',
keyboard: false,
size: 'lg',
resolve: {
periods() { return AccountingPeriod.query().$promise; },
lastClosingEnd() { return AccountingPeriod.lastClosingEnd().$promise; }
periods () { return AccountingPeriod.query().$promise; },
lastClosingEnd () { return AccountingPeriod.lastClosingEnd().$promise; }
}
});
}
};
$scope.toggleExportModal = function() {
$scope.toggleExportModal = function () {
$uibModal.open({
templateUrl: '<%= asset_path "admin/invoices/accountingExportModal.html" %>',
templateUrl: '/admin/invoices/accountingExportModal.html',
controller: 'AccountingExportModalController',
size: 'xl'
});
}
};
/**
* Test if the given date is within a closed accounting period
* @param date {Date} date to test
* @returns {boolean} true if closed, false otherwise
*/
$scope.isDateClosed = function(date) {
$scope.isDateClosed = function (date) {
for (const period of closedPeriods) {
if (moment(date).isBetween(moment.utc(period.start_at).startOf('day'), moment.utc(period.end_at).endOf('day'), null, '[]')) {
return true;
}
}
return false;
}
};
/**
* Callback to bulk save all settings in the page to the database with their values
*/
$scope.save = function() {
$scope.save = function () {
Setting.bulkUpdate(
{ settings: Object.values($scope.settings) },
function () { growl.success(_t('app.admin.invoices.codes_customization_success')); },
@ -625,29 +624,29 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
console.error(error);
}
);
}
};
/**
* Return the name of the operator that creates the invoice
*/
$scope.operatorName = function(invoice) {
$scope.operatorName = function (invoice) {
if (!invoice.operator) return '';
return `${invoice.operator.first_name} ${invoice.operator.last_name}`;
}
};
/**
* Open a modal dialog which ask for the stripe keys
* @param onlinePaymentModule {{name: String, value: String}} setting that defines the next status of the online payment module
* @return {boolean} false if the keys were not provided
*/
$scope.requireStripeKeys = function(onlinePaymentModule) {
$scope.requireStripeKeys = function (onlinePaymentModule) {
// if the online payment is about to be disabled, accept the change without any further question
if (onlinePaymentModule.value === false) return true;
// otherwise, open a modal to ask for the stripe keys
const modalInstance = $uibModal.open({
templateUrl: 'stripeKeys.html',
templateUrl: '/admin/invoices/settings/stripeKeys.html',
controller: 'StripeKeysModalController',
resolve: {
stripeKeys: ['Setting', function (Setting) { return Setting.query({ names: "['stripe_public_key', 'stripe_secret_key']" }).$promise; }]
@ -658,19 +657,19 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
if (success) {
Setting.get({ name: 'stripe_public_key' }, function (res) {
$scope.allSettings.stripe_public_key = res.setting.value;
})
});
Setting.isPresent({ name: 'stripe_secret_key' }, function (res) {
$scope.stripeSecretKey = (res.isPresent ? STRIPE_SK_HIDDEN : '');
})
});
Payment.onlinePaymentStatus(function (res) {
$scope.onlinePaymentStatus = res.status;
});
}
})
});
// return the promise
return modalInstance.result;
}
};
/**
* Setup the feature-tour for the admin/invoices page.
@ -814,7 +813,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
if (settings.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('invoices') < 0) {
uitour.start();
}
}
};
/* PRIVATE SCOPE */
@ -827,18 +826,18 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
// retrieve settings from the DB through the API
$scope.invoice.legals.content = settings['invoice_legals'];
$scope.invoice.text.content = settings['invoice_text'];
$scope.invoice.legals.content = settings.invoice_legals;
$scope.invoice.text.content = settings.invoice_text;
$scope.invoice.VAT.rate = parseFloat(settings['invoice_VAT-rate']);
$scope.invoice.VAT.active = (settings['invoice_VAT-active'] === 'true');
$scope.invoice.number.model = settings['invoice_order-nb'];
$scope.invoice.code.model = settings['invoice_code-value'];
$scope.invoice.code.active = (settings['invoice_code-active'] === 'true');
$scope.invoice.reference.model = settings['invoice_reference'];
$scope.invoice.reference.model = settings.invoice_reference;
$scope.invoice.logo = {
filetype: 'image/png',
filename: 'logo.png',
base64: settings['invoice_logo']
base64: settings.invoice_logo
};
// Watch the logo, when a change occurs, save it
@ -990,7 +989,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
$scope.ok = function () {
// check that at least 1 element of the invoice is refunded
$scope.avoir.invoice_items_ids = [];
for (let itemId in $scope.partial) {
for (const itemId in $scope.partial) {
if (Object.prototype.hasOwnProperty.call($scope.partial, itemId)) {
const refundItem = $scope.partial[itemId];
if (refundItem) {
@ -1024,14 +1023,14 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
* @param date {Date} date to test
* @returns {boolean} true if closed, false otherwise
*/
$scope.isDateClosed = function(date) {
$scope.isDateClosed = function (date) {
for (const period of closedPeriods) {
if (moment(date).isBetween(moment.utc(period.start_at).startOf('day'), moment.utc(period.end_at).endOf('day'), null, '[]')) {
return true;
}
}
return false;
}
};
/* PRIVATE SCOPE */
@ -1058,11 +1057,10 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
}
]);
/**
* Controller used in the modal window allowing an admin to close an accounting period
*/
Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$uibModalInstance', '$window', '$sce', 'Invoice', 'AccountingPeriod', 'periods', 'lastClosingEnd','dialogs', 'growl', '_t',
Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$uibModalInstance', '$window', '$sce', 'Invoice', 'AccountingPeriod', 'periods', 'lastClosingEnd', 'dialogs', 'growl', '_t',
function ($scope, $uibModalInstance, $window, $sce, Invoice, AccountingPeriod, periods, lastClosingEnd, dialogs, growl, _t) {
const YESTERDAY = moment.utc({ h: 0, m: 0, s: 0, ms: 0 }).subtract(1, 'day').toDate();
const LAST_CLOSING = moment.utc(lastClosingEnd.last_end_date).toDate();
@ -1124,11 +1122,11 @@ Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$ui
_t(
'app.admin.invoices.confirm_close_START_END',
{ START: moment.utc($scope.period.start_at).format('LL'), END: moment.utc($scope.period.end_at).format('LL') }
)
+ '<br/><br/><strong>'
+ _t('app.admin.invoices.period_must_match_fiscal_year')
+ '</strong><br/><br/>'
+ _t('app.admin.invoices.this_may_take_a_while')
) +
'<br/><br/><strong>' +
_t('app.admin.invoices.period_must_match_fiscal_year') +
'</strong><br/><br/>' +
_t('app.admin.invoices.this_may_take_a_while')
)
};
}
@ -1151,7 +1149,7 @@ Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$ui
));
$uibModalInstance.close(resp);
},
function(error) {
function (error) {
$scope.pendingCreation = false;
growl.error(_t('app.admin.invoices.failed_to_close_period'));
$scope.errors = error.data;
@ -1159,7 +1157,6 @@ Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$ui
);
}
);
};
/**
@ -1170,147 +1167,145 @@ Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$ui
/**
* Trigger the API call to download the JSON archive of the closed accounting period
*/
$scope.downloadArchive = function(period) {
$scope.downloadArchive = function (period) {
$window.location.href = `/api/accounting_periods/${period.id}/archive`;
}
};
}
]);
Application.Controllers.controller('AccountingExportModalController', ['$scope', '$uibModalInstance', 'Invoice', 'Export', 'CSRF', 'growl', '_t',
function ($scope, $uibModalInstance, Invoice, Export, CSRF, growl, _t) {
// Retrieve Anti-CSRF tokens from cookies
CSRF.setMetaTags();
CSRF.setMetaTags();
const SETTINGS = {
acd: {
format: 'csv',
encoding: 'ISO-8859-1',
separator: ';',
dateFormat: '%d/%m/%Y',
labelMaxLength: 50,
decimalSeparator: ',',
exportInvoicesAtZero: false,
columns: ['journal_code', 'date', 'account_code', 'account_label', 'piece', 'line_label', 'debit_origin', 'credit_origin', 'debit_euro', 'credit_euro', 'lettering']
}
};
const SETTINGS = {
acd: {
format: 'csv',
encoding: 'ISO-8859-1',
separator: ';',
dateFormat: '%d/%m/%Y',
labelMaxLength: 50,
decimalSeparator: ',',
exportInvoicesAtZero: false,
columns: ['journal_code', 'date', 'account_code', 'account_label', 'piece', 'line_label', 'debit_origin', 'credit_origin', 'debit_euro', 'credit_euro', 'lettering']
}
};
/* PUBLIC SCOPE */
/* PUBLIC SCOPE */
// API URL where the form will be posted
$scope.actionUrl = '/api/accounting/export';
// API URL where the form will be posted
$scope.actionUrl = '/api/accounting/export';
// Form action on the above URL
$scope.method = 'post';
// Form action on the above URL
$scope.method = 'post';
// Anti-CSRF token to inject into the download form
$scope.csrfToken = angular.element('meta[name="csrf-token"]')[0].content;
// Anti-CSRF token to inject into the download form
$scope.csrfToken = angular.element('meta[name="csrf-token"]')[0].content;
// API request body to generate the export
$scope.query = null;
// API request body to generate the export
$scope.query = null;
// binding to radio button "export to"
$scope.exportTarget = {
software: null,
startDate: null,
endDate: null,
settings: null
};
// binding to radio button "export to"
$scope.exportTarget = {
software: null,
startDate: null,
endDate: null,
settings: null
};
// AngularUI-Bootstrap datepicker parameters to define export dates range
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: { // default: datePickers are not shown
start: false,
end: false
},
options: {
startingDay: Fablab.weekStartingDay
}
};
// AngularUI-Bootstrap datepicker parameters to define export dates range
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: { // default: datePickers are not shown
start: false,
end: false
},
options: {
startingDay: Fablab.weekStartingDay
}
};
// Date of the first invoice
$scope.firstInvoice = null;
// Date of the first invoice
$scope.firstInvoice = null;
/**
/**
* Validate the export
*/
$scope.ok = function () {
const statusQry = mkQuery();
$scope.query = statusQry;
$scope.ok = function () {
const statusQry = mkQuery();
$scope.query = statusQry;
Export.status(statusQry).then(function (res) {
if (!res.data.exists) {
growl.success(_t('app.admin.invoices.export_is_running'));
}
$uibModalInstance.close(res);
});
};
Export.status(statusQry).then(function (res) {
if (!res.data.exists) {
growl.success(_t('app.admin.invoices.export_is_running'));
}
$uibModalInstance.close(res);
});
};
/**
/**
* Callback to open/close one of the datepickers
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
* @param picker {string} start | end
*/
$scope.toggleDatePicker = function(event, picker) {
event.preventDefault();
$scope.datePicker.opened[picker] = !$scope.datePicker.opened[picker];
};
$scope.toggleDatePicker = function (event, picker) {
event.preventDefault();
$scope.datePicker.opened[picker] = !$scope.datePicker.opened[picker];
};
/**
/**
* Will fill the export settings, according to the selected software
* @param software {String} must be one of SETTINGS.*
*/
$scope.fillSettings = function(software) {
$scope.exportTarget.settings = SETTINGS[software];
};
$scope.fillSettings = function (software) {
$scope.exportTarget.settings = SETTINGS[software];
};
/**
/**
* Just dismiss the modal window
*/
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
/* PRIVATE SCOPE */
/* PRIVATE SCOPE */
/**
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
const initialize = function () {
// if the invoice was payed with stripe, allow to refund through stripe
Invoice.first(function (data) {
$scope.firstInvoice = data.date;
$scope.exportTarget.startDate = data.date;
$scope.exportTarget.endDate = moment().toISOString();
});
};
Invoice.first(function (data) {
$scope.firstInvoice = data.date;
$scope.exportTarget.startDate = data.date;
$scope.exportTarget.endDate = moment().toISOString();
});
};
/**
/**
* Prepare the query for the export API
* @returns {{extension: *, query: *, category: string, type: *, key: *}}
*/
const mkQuery = function() {
return {
category: 'accounting',
type: $scope.exportTarget.software,
extension: $scope.exportTarget.settings.format,
key: $scope.exportTarget.settings.separator,
query: JSON.stringify({
columns: $scope.exportTarget.settings.columns,
encoding: $scope.exportTarget.settings.encoding,
date_format: $scope.exportTarget.settings.dateFormat,
start_date: $scope.exportTarget.startDate,
end_date: $scope.exportTarget.endDate,
label_max_length: $scope.exportTarget.settings.labelMaxLength,
decimal_separator: $scope.exportTarget.settings.decimalSeparator,
export_invoices_at_zero: $scope.exportTarget.settings.exportInvoicesAtZero
})
const mkQuery = function () {
return {
category: 'accounting',
type: $scope.exportTarget.software,
extension: $scope.exportTarget.settings.format,
key: $scope.exportTarget.settings.separator,
query: JSON.stringify({
columns: $scope.exportTarget.settings.columns,
encoding: $scope.exportTarget.settings.encoding,
date_format: $scope.exportTarget.settings.dateFormat,
start_date: $scope.exportTarget.startDate,
end_date: $scope.exportTarget.endDate,
label_max_length: $scope.exportTarget.settings.labelMaxLength,
decimal_separator: $scope.exportTarget.settings.decimalSeparator,
export_invoices_at_zero: $scope.exportTarget.settings.exportInvoicesAtZero
})
};
};
}
// !!! MUST BE CALLED AT THE END of the controller
return initialize();
}]);
// !!! MUST BE CALLED AT THE END of the controller
return initialize();
}]);
/**
* Controller used in the modal window allowing an admin to close an accounting period
@ -1343,15 +1338,14 @@ Application.Controllers.controller('StripeKeysModalController', ['$scope', '$uib
method: 'GET',
url: 'https://api.stripe.com/v1/charges',
headers: {
Authorization: `Bearer ${$scope.secretKey}`,
Authorization: `Bearer ${$scope.secretKey}`
}
}).then(function () {
$scope.secretKeyStatus = true;
}, function (err) {
if (err.status === 401) $scope.secretKeyStatus = false;
});
}
};
/**
* Trigger the test of the secret key and set the result in $scope.secretKeyStatus
@ -1370,14 +1364,14 @@ Application.Controllers.controller('StripeKeysModalController', ['$scope', '$uib
'Content-Type': 'application/x-www-form-urlencoded'
},
data: $httpParamSerializerJQLike({
'pii[id_number]': 'test',
'pii[id_number]': 'test'
})
}).then(function () {
$scope.publicKeyStatus = true;
}, function (err) {
if (err.status === 401) $scope.publicKeyStatus = false;
});
}
};
/**
* Validate the keys
@ -1385,7 +1379,8 @@ Application.Controllers.controller('StripeKeysModalController', ['$scope', '$uib
$scope.ok = function () {
if ($scope.secretKeyStatus && $scope.publicKeyStatus) {
Setting.bulkUpdate(
{ settings: [
{
settings: [
{
name: 'stripe_public_key',
value: $scope.publicKey
@ -1394,7 +1389,8 @@ Application.Controllers.controller('StripeKeysModalController', ['$scope', '$uib
name: 'stripe_secret_key',
value: $scope.secretKey
}
] },
]
},
function () {
growl.success(_t('app.admin.invoices.payment.stripe_keys_saved'));
$uibModalInstance.close(true);
@ -1405,7 +1401,7 @@ Application.Controllers.controller('StripeKeysModalController', ['$scope', '$uib
}
);
} else {
growl.error(_t('app.admin.invoices.payment.error_check_keys'))
growl.error(_t('app.admin.invoices.payment.error_check_keys'));
}
};

View File

@ -236,7 +236,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
$scope.openPartnerNewModal = function () {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/_partner_new_modal.html" %>',
templateUrl: '/shared/_partner_new_modal.html',
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'User', function ($scope, $uibModalInstance, User) {
$scope.partner = {};
@ -702,7 +702,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.changeUserRole = function() {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "admin/members/change_role_modal.html" %>',
templateUrl: '/admin/members/change_role_modal.html',
size: 'lg',
resolve: {
user() { return $scope.user; }
@ -750,7 +750,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.updateSubscriptionModal = function (subscription, free) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "admin/subscriptions/expired_at_modal.html" %>',
templateUrl: '/admin/subscriptions/expired_at_modal.html',
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'Subscription', function ($scope, $uibModalInstance, Subscription) {
$scope.new_expired_at = angular.copy(subscription.expired_at);
@ -797,7 +797,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.createSubscriptionModal = function (user, plans) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "admin/subscriptions/create_modal.html" %>',
templateUrl: '/admin/subscriptions/create_modal.html',
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', function ($scope, $uibModalInstance, Subscription, Group) {
// selected user
@ -845,7 +845,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.createWalletCreditModal = function (user, wallet) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "wallet/credit_modal.html" %>',
templateUrl: '/wallet/credit_modal.html',
controller: ['$scope', '$uibModalInstance', 'Wallet', function ($scope, $uibModalInstance, Wallet) {
// default: do not generate a refund invoice
$scope.generate_avoir = false;

View File

@ -114,7 +114,7 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
$scope.openPartnerNewModal = function (subscription) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/_partner_new_modal.html" %>',
templateUrl: '/shared/_partner_new_modal.html',
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'User', function ($scope, $uibModalInstance, User) {
$scope.partner = {};

View File

@ -570,7 +570,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
*/
$scope.sendCouponToUser = function (coupon) {
$uibModal.open({
templateUrl: '<%= asset_path "admin/pricing/sendCoupon.html" %>',
templateUrl: '/admin/pricing/sendCoupon.html',
resolve: {
coupon () { return coupon; }
},

View File

@ -173,7 +173,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
$scope.savePrivacyPolicy = function () {
// open modal
const modalInstance = $uibModal.open({
templateUrl: '<%= asset_path "admin/settings/save_policy.html" %>',
templateUrl: '/admin/settings/save_policy.html',
controller: 'SavePolicyController',
resolve: {
saveCb () { return $scope.save; },
@ -274,7 +274,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
*/
$scope.analyticsModal = function() {
$uibModal.open({
templateUrl: '<%= asset_path "admin/settings/analyticsModal.html" %>',
templateUrl: '/admin/settings/analyticsModal.html',
controller: 'AnalyticsModalController',
size: 'lg',
resolve: {

View File

@ -311,7 +311,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
*/
$scope.exportToExcel = function () {
const options = {
templateUrl: '<%= asset_path "admin/statistics/export.html" %>',
templateUrl: '/admin/statistics/export.html',
size: 'sm',
controller: 'ExportStatisticsController',
resolve: {

View File

@ -221,7 +221,7 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
*/
$scope.showReservations = function (training, availability) {
$uibModal.open({
templateUrl: '<%= asset_path "admin/trainings/validTrainingModal.html" %>',
templateUrl: '/admin/trainings/validTrainingModal.html',
controller: ['$scope', '$uibModalInstance', function ($scope, $uibModalInstance) {
$scope.availability = availability;

View File

@ -89,7 +89,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
<% else %>
return $uibModal.open({
templateUrl: '<%= asset_path "shared/signupModal.html" %>',
templateUrl: '/shared/signupModal.html',
size: 'md',
resolve: {
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'recaptcha_site_key', 'confirmation_required']" }).$promise; }]
@ -192,7 +192,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
*/
$scope.editPassword = function (token) {
$uibModal.open({
templateUrl: '<%= asset_path "shared/passwordEditModal.html" %>',
templateUrl: '/shared/passwordEditModal.html',
size: 'md',
controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) {
$scope.user = { reset_password_token: token };
@ -282,7 +282,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
if ($rootScope.currentUser.role !== 'admin') return;
$uibModal.open({
templateUrl: '<%= asset_path "admin/versions/upgradeModal.html" %>',
templateUrl: '/admin/versions/upgradeModal.html',
controller: 'VersionModalController',
resolve: {
version() { return $scope.version; }
@ -407,7 +407,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
$window.location.href = '/sso-redirect';
<% else %>
return $uibModal.open({
templateUrl: '<%= asset_path "shared/deviseModal.html" %>',
templateUrl: '/shared/deviseModal.html',
size: 'sm',
resolve: {
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['confirmation_required']" }).$promise; }]
@ -470,7 +470,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
} else if (reason === 'resetPassword') {
// open the 'reset password' modal
return $uibModal.open({
templateUrl: '<%= asset_path "shared/passwordNewModal.html" %>',
templateUrl: '/shared/passwordNewModal.html',
size: 'sm',
controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) {
$scope.user = { email: '' };
@ -488,7 +488,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
} else if (reason === 'confirmationNew') {
// open the 'reset password' modal
return $uibModal.open({
templateUrl: '<%= asset_path "shared/ConfirmationNewModal.html" %>',
templateUrl: '/shared/ConfirmationNewModal.html',
size: 'sm',
controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) {
$scope.user = { email: '' };

View File

@ -55,7 +55,12 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
evt: filter.evt,
dispo: filter.dispo
});
$scope.calendarConfig.events = availabilitySourceUrl();
// remove all
$scope.eventSources.splice(0, $scope.eventSources.length);
// recreate source for trainings/machines/events with new filters
$scope.eventSources.push({
url: availabilitySourceUrl()
});
// external iCalendar events sources
$scope.externals.forEach(e => {
if (e.checked) {
@ -74,7 +79,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
}
}
});
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEventSources');
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
};
/**
@ -84,7 +89,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
$scope.calendarStyle = function (calendar) {
return {
'border-color': calendar.color,
'color': calendar.text_color
color: calendar.text_color
};
};
@ -101,12 +106,12 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
// toggle to select all formation/machine
$scope.toggleFilter = function (type, filter) {
$scope[type].forEach(t => t.checked = filter[type]);
return $scope.filterAvailabilities(filter, $scope);
$scope.filterAvailabilities(filter, $scope);
};
$scope.openFilterAside = () =>
$aside.open({
templateUrl: 'filterAside.html',
templateUrl: '/calendar/filterAside.html',
placement: 'right',
size: 'md',
backdrop: false,
@ -148,8 +153,8 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
$uibModalInstance.dismiss();
return e.stopPropagation();
};
}
] });
}]
});
/* PRIVATE SCOPE */
@ -159,7 +164,6 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
const initialize = () => {
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
events: availabilitySourceUrl(),
slotEventOverlap: true,
header: {
left: 'month agendaWeek agendaDay',
@ -175,10 +179,13 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
viewRender (view, element) {
return viewRenderCb(view, element);
},
eventRender (event, element, view) {
eventRender (event, element) {
return eventRenderCb(event, element);
}
});
$scope.eventSources = [{
url: availabilitySourceUrl()
}];
$scope.externals.forEach(e => {
if (e.checked) {
$scope.eventSources.push({
@ -194,7 +201,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
/**
* Callback triggered when an event object is clicked in the fullCalendar view
*/
const calendarEventClickCb = function (event, jsEvent, view) {
const calendarEventClickCb = function (event) {
// current calendar object
const { calendar } = uiCalendarConfig.calendars;
if (event.available_type === 'machines') {
@ -240,7 +247,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
* This function is called when calendar view is rendered or changed
* @see https://fullcalendar.io/docs/v3/viewRender#v2
*/
const viewRenderCb = function (view, element) {
const viewRenderCb = function (view) {
toggleSlotEventOverlap(view);
if (view.type === 'agendaDay') {
// get availabilties by 1 day for show machine slots
@ -255,7 +262,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
const eventRenderCb = function (event, element) {
if (event.tags && event.tags.length > 0) {
let html = '';
for (let tag of Array.from(event.tags)) {
for (const tag of Array.from(event.tags)) {
html += `<span class='label label-success text-white'>${tag.name}</span> `;
}
element.find('.fc-title').append(`<br/>${html}`);
@ -269,7 +276,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
return { t, m, s, evt: $scope.filter.evt, dispo: $scope.filter.dispo };
};
var availabilitySourceUrl = () => `/api/availabilities/public?${$.param(getFilter())}`;
const availabilitySourceUrl = () => `/api/availabilities/public?${$.param(getFilter())}`;
// !!! MUST BE CALLED AT THE END of the controller
return initialize();

View File

@ -42,7 +42,7 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
age_range_id: null
};
$scope.monthNames = [<%= t('date.month_names')[1..-1].map { |m| "\"#{m}\"" }.join(', ') %>];
$scope.monthNames = [<%= I18n.t('date.month_names')[1..-1].map { |m| "\"#{m}\"" }.join(', ') %>];
/**
* Adds a resultset of events to the bottom of the page, grouped by month
@ -186,7 +186,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// open a confirmation dialog
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "events/deleteRecurrent.html" %>',
templateUrl: '/events/deleteRecurrent.html',
size: 'md',
controller: 'DeleteRecurrentEventController',
resolve: {
@ -416,7 +416,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
$scope.modifyReservation = function (reservation) {
const index = $scope.reservations.indexOf(reservation);
return $uibModal.open({
templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>',
templateUrl: '/events/modify_event_reservation_modal.html',
resolve: {
event () { return $scope.event; },
reservation () { return reservation; }
@ -671,7 +671,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
*/
const payByStripe = function (reservation) {
$uibModal.open({
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
templateUrl: '/stripe/payment_modal.html',
size: 'md',
resolve: {
reservation () {
@ -739,7 +739,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
*/
const payOnSite = function (reservation) {
$uibModal.open({
templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>',
templateUrl: '/shared/valid_reservation_modal.html',
size: 'sm',
resolve: {
reservation () {
@ -858,7 +858,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
const showReserveSlotSameTimeModal = function(sameTimeReservations, callback) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/_reserve_slot_same_time.html" %>',
templateUrl: '/shared/_reserve_slot_same_time.html',
size: 'md',
controller: 'ReserveSlotSameTimeController',
resolve: {

View File

@ -121,7 +121,7 @@ const _reserveMachine = function (machine, e) {
// the training before he can book the reservation
if (machine.current_user_training_reservation) {
return _this.$uibModal.open({
templateUrl: '<%= asset_path "machines/training_reservation_modal.html" %>',
templateUrl: '/machines/training_reservation_modal.html',
controller: ['$scope', '$uibModalInstance', function ($scope, $uibModalInstance) {
$scope.machine = machine;
return $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
@ -136,7 +136,7 @@ const _reserveMachine = function (machine, e) {
// otherwise open the information modal
} else {
return _this.$uibModal.open({
templateUrl: '<%= asset_path "machines/request_training_modal.html" %>',
templateUrl: '/machines/request_training_modal.html',
controller: ['$scope', '$uibModalInstance', '$state', function ($scope, $uibModalInstance, $state) {
$scope.machine = machine;
$scope.member = _this.$scope.currentUser;
@ -495,7 +495,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = FREE_SLOT_BORDER_COLOR;
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_reserve');
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
@ -506,11 +506,11 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
slot.borderColor = FREE_SLOT_BORDER_COLOR;
slot.title = '';
slot.isValid = false;
slot.id = null;
slot.slot_id = null;
slot.is_reserved = false;
slot.can_modify = false;
slot.offered = false;
return updateCalendar();
updateEvents(slot);
};
/**
@ -524,7 +524,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_change');
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
@ -534,34 +534,45 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = '';
updateEvents($scope.events.placable);
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_shift');
updateEvents($scope.selectedEvent);
}
return updateCalendar();
return true;
};
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyMachineSlot = function () {
$scope.events.placable.title = $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
$scope.events.placable.user = angular.copy($scope.events.modifiable.user);
const save = {
slotId: $scope.events.modifiable.slot_id,
borderColor: $scope.events.modifiable.borderColor,
user: angular.copy($scope.events.modifiable.user),
title: $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available')
};
$scope.events.modifiable.backgroundColor = 'white';
$scope.events.modifiable.title = '';
$scope.events.modifiable.borderColor = FREE_SLOT_BORDER_COLOR;
$scope.events.modifiable.id = null;
$scope.events.modifiable.slot_id = null;
$scope.events.modifiable.is_reserved = false;
$scope.events.modifiable.can_modify = false;
updateEvents($scope.events.modifiable);
return updateCalendar();
$scope.events.placable.title = save.title;
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = save.borderColor;
$scope.events.placable.slot_id = save.slotId;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
$scope.events.placable.user = angular.copy(save.user);
updateEvents($scope.events.placable);
refetchCalendar();
};
/**
@ -575,7 +586,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.events.modifiable.title = $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
$scope.events.modifiable.backgroundColor = 'white';
return updateCalendar();
updateEvents($scope.events.placable, $scope.events.modifiable);
};
/**
@ -648,7 +659,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.selectedPlan = null;
}
return refetchCalendar();
refetchCalendar();
};
/**
@ -662,11 +673,13 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
Availability.machine({ machineId: $stateParams.id }, function (availabilities) {
$scope.eventSources.push({
events: availabilities,
textColor: 'black'
});
$scope.eventSources.push({
events: function (start, end, timezone, callback) {
Availability.machine({ machineId: $stateParams.id }, function (availabilities) {
callback(availabilities);
});
},
textColor: 'black'
});
if ($scope.currentUser.role !== 'admin') {
@ -680,7 +693,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
* if it's too late).
*/
var calendarEventClickCb = function (event, jsEvent, view) {
const calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event;
return $scope.selectionTime = new Date();
};
@ -690,7 +703,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element) {
const eventRenderCb = function (event, element) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = '';
for (let tag of Array.from(event.tags)) {
@ -708,28 +721,30 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* @param reservation {Object}
* @param user {Object} user associated with the slot
*/
var updateMachineSlot = function (slot, reservation, user) {
const updateMachineSlot = function (slot, reservation, user) {
angular.forEach(reservation.slots, function (s) {
if (slot.start.isSame(s.start_at)) {
slot.id = s.id;
return slot.user = user;
slot.slot_id = s.id;
slot.user = user;
}
});
updateEvents(slot);
};
/**
* Update the calendar's display to render the new attributes of the events
* @param events Object[] events to update in full-calendar
*/
var updateCalendar = function () { uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents'); };
const updateEvents = function (...events) {
const realEvents = events.filter(e => !_.isNil(e));
uiCalendarConfig.calendars.calendar.fullCalendar('updateEvents', realEvents);
};
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = function () {
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents');
});
const refetchCalendar = function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
};
// !!! MUST BE CALLED AT THE END of the controller

View File

@ -235,7 +235,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
*/
const payByStripe = function () {
$uibModal.open({
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
templateUrl: '/stripe/payment_modal.html',
size: 'md',
resolve: {
selectedPlan () { return $scope.selectedPlan; },
@ -299,7 +299,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
*/
const payOnSite = function () {
$uibModal.open({
templateUrl: '<%= asset_path "plans/payment_modal.html" %>',
templateUrl: '/plans/payment_modal.html',
size: 'sm',
resolve: {
selectedPlan () { return $scope.selectedPlan; },

View File

@ -167,7 +167,7 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
event.stopPropagation();
dialogs.confirm(
{
templateUrl: '<%= asset_path "profile/resend_code_modal.html" %>',
templateUrl: '/profile/resend_code_modal.html',
resolve: {
object () {
return { email: memberPromise.email };

View File

@ -588,7 +588,7 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
if (e) { e.preventDefault(); }
$uibModal.open({
templateUrl: '<%= asset_path "shared/signalAbuseModal.html" %>',
templateUrl: '/shared/signalAbuseModal.html',
size: 'md',
resolve: {
project () { return $scope.project; }

View File

@ -307,8 +307,8 @@ Application.Controllers.controller('ShowSpaceController', ['$scope', '$state', '
* per slots.
*/
Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateParams', 'Auth', '$timeout', 'Availability', 'Member', 'availabilitySpacesPromise', 'plansPromise', 'groupsPromise', 'settingsPromise', 'spacePromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, Auth, $timeout, Availability, Member, availabilitySpacesPromise, plansPromise, groupsPromise, settingsPromise, spacePromise, _t, uiCalendarConfig, CalendarConfig) {
Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateParams', 'Auth', '$timeout', 'Availability', 'Member', 'plansPromise', 'groupsPromise', 'settingsPromise', 'spacePromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, Auth, $timeout, Availability, Member, plansPromise, groupsPromise, settingsPromise, spacePromise, _t, uiCalendarConfig, CalendarConfig) {
/* PRIVATE STATIC CONSTANTS */
// Color of the selected event backgound
@ -323,7 +323,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
/* PUBLIC SCOPE */
// bind the spaces availabilities with full-Calendar events
$scope.eventSources = [ { events: availabilitySpacesPromise, textColor: 'black' } ];
$scope.eventSources = [];
// the user to deal with, ie. the current user for non-admins
$scope.ctrl =
@ -394,7 +394,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
*/
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = SELECTED_EVENT_BG_COLOR;
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
@ -404,13 +404,13 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
slot.backgroundColor = 'white';
slot.title = '';
slot.borderColor = FREE_SLOT_BORDER_COLOR;
slot.id = null;
slot.slot_id = null;
slot.isValid = false;
slot.is_reserved = false;
slot.can_modify = false;
slot.offered = false;
if (slot.is_completed) { slot.is_completed = false; }
return updateCalendar();
updateEvents(slot);
};
/**
@ -424,50 +424,60 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('app.logged.space_reserve.i_change');
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
*/
$scope.changeModifyTrainingSlot = function () {
$scope.changeModifySpaceSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = '';
updateEvents($scope.events.placable);
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('app.logged.space_reserve.i_shift');
updateEvents($scope.selectedEvent);
}
return updateCalendar();
return true;
};
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyTrainingSlot = function () {
$scope.events.placable.title = _t('app.logged.space_reserve.i_ve_reserved');
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
$scope.modifySpaceSlot = function () {
const save = {
slotId: $scope.events.modifiable.slot_id,
borderColor: $scope.events.modifiable.borderColor,
title: _t('app.logged.space_reserve.i_ve_reserved')
};
$scope.events.modifiable.backgroundColor = 'white';
$scope.events.modifiable.title = '';
$scope.events.modifiable.borderColor = FREE_SLOT_BORDER_COLOR;
$scope.events.modifiable.id = null;
$scope.events.modifiable.slot_id = null;
$scope.events.modifiable.is_reserved = false;
$scope.events.modifiable.can_modify = false;
if ($scope.events.modifiable.is_completed) { $scope.events.modifiable.is_completed = false; }
updateEvents($scope.events.modifiable);
return updateCalendar();
$scope.events.placable.title = save.title;
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = save.borderColor;
$scope.events.placable.slot_id = save.slotId;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
updateEvents($scope.events.placable);
refetchCalendar();
};
/**
* Cancel the current booking modification, reseting the whole process
*/
$scope.cancelModifyTrainingSlot = function () {
$scope.cancelModifySpaceSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = '';
@ -475,7 +485,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
$scope.events.modifiable.title = _t('app.logged.space_reserve.i_ve_reserved');
$scope.events.modifiable.backgroundColor = 'white';
return updateCalendar();
updateEvents($scope.events.placable, $scope.events.modifiable);
};
/**
@ -544,7 +554,8 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
spaceSlot.title = _t('app.logged.space_reserve.i_ve_reserved');
spaceSlot.backgroundColor = 'white';
spaceSlot.borderColor = RESERVED_SLOT_BORDER_COLOR;
return updateSpaceSlotId(spaceSlot, reservation);
updateSpaceSlotId(spaceSlot, reservation);
updateEvents(spaceSlot);
});
if ($scope.selectedPlan) {
@ -558,7 +569,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
Auth._currentUser.training_credits = angular.copy(reservation.user.training_credits);
Auth._currentUser.machine_credits = angular.copy(reservation.user.machine_credits);
return refetchCalendar();
refetchCalendar();
};
/**
@ -573,8 +584,18 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
*/
const initialize = function () {
if ($scope.currentUser.role !== 'admin') {
return Member.get({ id: $scope.currentUser.id }, function (member) { $scope.ctrl.member = member; });
Member.get({ id: $scope.currentUser.id }, function (member) { $scope.ctrl.member = member; });
}
// we load the availabilities from a callback function of the $scope.eventSources, instead of resolving a promise
// in the router because this allows to refetchEvents from fullCalendar API.
$scope.eventSources.push({
events: function (start, end, timezone, callback) {
Availability.spaces({ spaceId: $stateParams.id }, function (availabilities) {
callback(availabilities);
});
},
textColor: 'black'
});
};
/**
@ -584,12 +605,9 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
* if it's too late).
* @see http://fullcalendar.io/docs/mouse/eventClick/
*/
var calendarEventClickCb = function (event, jsEvent, view) {
const calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event;
if ($stateParams.id === 'all') {
$scope.training = event.training;
}
return $scope.selectionTime = new Date();
$scope.selectionTime = new Date();
};
/**
@ -597,7 +615,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element, view) {
const eventRenderCb = function (event, element, view) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = '';
for (let tag of Array.from(event.tags)) {
@ -613,27 +631,29 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
* @param slot {Object}
* @param reservation {Object}
*/
var updateSpaceSlotId = function (slot, reservation) {
const updateSpaceSlotId = function (slot, reservation) {
angular.forEach(reservation.slots, function (s) {
if (slot.start_at === slot.start_at) {
return slot.id = s.id;
if (slot.start.isSame(s.start_at)) {
slot.slot_id = s.id;
}
});
};
/**
* Update the calendar's display to render the new attributes of the events
* @param events Object[] events to update in full-calendar
*/
var updateCalendar = function () { uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents'); };
const updateEvents = function (...events) {
const realEvents = events.filter(e => !_.isNil(e));
uiCalendarConfig.calendars.calendar.fullCalendar('updateEvents', realEvents);
};
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = function () {
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents');
});
const refetchCalendar = function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
};
// !!! MUST BE CALLED AT THE END of the controller

View File

@ -91,8 +91,8 @@ Application.Controllers.controller('ShowTrainingController', ['$scope', '$state'
* training can be reserved during the reservation process (the shopping cart may contains only one training and a subscription).
*/
Application.Controllers.controller('ReserveTrainingController', ['$scope', '$stateParams', 'Auth', '$timeout', 'Availability', 'Member', 'availabilityTrainingsPromise', 'plansPromise', 'groupsPromise', 'settingsPromise', 'trainingPromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, Auth, $timeout, Availability, Member, availabilityTrainingsPromise, plansPromise, groupsPromise, settingsPromise, trainingPromise, _t, uiCalendarConfig, CalendarConfig) {
Application.Controllers.controller('ReserveTrainingController', ['$scope', '$stateParams', 'Auth', 'AuthService', '$timeout', 'Availability', 'Member', 'plansPromise', 'groupsPromise', 'settingsPromise', 'trainingPromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, Auth, AuthService, $timeout, Availability, Member, plansPromise, groupsPromise, settingsPromise, trainingPromise, _t, uiCalendarConfig, CalendarConfig) {
/* PRIVATE STATIC CONSTANTS */
// Color of the selected event backgound
@ -104,7 +104,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
/* PUBLIC SCOPE */
// bind the trainings availabilities with full-Calendar events
$scope.eventSources = [ { events: availabilityTrainingsPromise, textColor: 'black' } ];
$scope.eventSources = [];
// the user to deal with, ie. the current user for non-admins
$scope.ctrl =
@ -177,15 +177,15 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$scope.trainingInformationMessage = settingsPromise.training_information_message;
/**
* Change the last selected slot's appearence to looks like 'added to cart'
* Change the last selected slot's appearance to looks like 'added to cart'
*/
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = SELECTED_EVENT_BG_COLOR;
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
* Change the last selected slot's appearence to looks like 'never added to cart'
* Change the last selected slot's appearance to looks like 'never added to cart'
*/
$scope.markSlotAsRemoved = function (slot) {
slot.backgroundColor = 'white';
@ -197,7 +197,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
slot.can_modify = false;
slot.offered = false;
if (slot.is_completed) { slot.is_completed = false; }
return updateCalendar();
updateEvents(slot);
};
/**
@ -211,44 +211,56 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('app.logged.trainings_reserve.i_change');
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
*/
$scope.changeModifyTrainingSlot = function () {
if ($scope.selectedEvent.training.id !== $scope.events.modifiable.training.id) return false;
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = $scope.events.placable.training.name;
updateEvents($scope.events.placable);
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('app.logged.trainings_reserve.i_shift');
updateEvents($scope.selectedEvent);
}
return updateCalendar();
return true;
};
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyTrainingSlot = function () {
$scope.events.placable.title = $scope.currentUser.role !== 'admin' ? $scope.events.placable.training.name + ' - ' + _t('app.logged.trainings_reserve.i_ve_reserved') : $scope.events.placable.training.name;
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
const save = {
slotId: $scope.events.modifiable.slot_id,
borderColor: $scope.events.modifiable.borderColor,
title: !AuthService.isAuthorized(['admin', 'manager']) ? $scope.events.placable.training.name + ' - ' + _t('app.logged.trainings_reserve.i_ve_reserved') : $scope.events.placable.training.name,
};
$scope.events.modifiable.backgroundColor = 'white';
$scope.events.modifiable.title = $scope.events.modifiable.training.name;
$scope.events.modifiable.borderColor = FREE_SLOT_BORDER_COLOR;
$scope.events.modifiable.id = null;
$scope.events.modifiable.slot_id = null;
$scope.events.modifiable.is_reserved = false;
$scope.events.modifiable.can_modify = false;
if ($scope.events.modifiable.is_completed) { $scope.events.modifiable.is_completed = false; }
updateEvents($scope.events.modifiable);
return updateCalendar();
$scope.events.placable.title = save.title;
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = save.borderColor;
$scope.events.placable.slot_id = save.slotId;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
updateEvents($scope.events.placable);
refetchCalendar();
};
/**
@ -262,7 +274,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$scope.events.modifiable.title = $scope.currentUser.role !== 'admin' ? $scope.events.modifiable.training.name + ' - ' + _t('app.logged.trainings_reserve.i_ve_reserved') : $scope.events.modifiable.training.name;
$scope.events.modifiable.backgroundColor = 'white';
return updateCalendar();
updateEvents($scope.events.placable, $scope.events.modifiable);
};
/**
@ -326,12 +338,15 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
* @param reservation {Object}
*/
$scope.afterPayment = function (reservation) {
$scope.events.paid[0].backgroundColor = 'white';
$scope.events.paid[0].is_reserved = true;
$scope.events.paid[0].can_modify = true;
updateTrainingSlotId($scope.events.paid[0], reservation);
$scope.events.paid[0].borderColor = '#b2e774';
$scope.events.paid[0].title = $scope.events.paid[0].training.name + ' - ' + _t('app.logged.trainings_reserve.i_ve_reserved');
angular.forEach($scope.events.paid, function (trainingSlot, key) {
trainingSlot.backgroundColor = 'white';
trainingSlot.is_reserved = true;
trainingSlot.can_modify = true;
updateTrainingSlotId(trainingSlot, reservation);
trainingSlot.borderColor = '#b2e774';
trainingSlot.title = trainingSlot.training.name + ' - ' + _t('app.logged.trainings_reserve.i_ve_reserved');
updateEvents(trainingSlot);
});
if ($scope.selectedPlan) {
$scope.ctrl.member.subscribed_plan = angular.copy($scope.selectedPlan);
@ -344,7 +359,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
Auth._currentUser.training_credits = angular.copy(reservation.user.training_credits);
Auth._currentUser.machine_credits = angular.copy(reservation.user.machine_credits);
return refetchCalendar();
refetchCalendar();
};
/**
@ -361,6 +376,16 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
if ($scope.currentUser.role !== 'admin') {
return Member.get({ id: $scope.currentUser.id }, function (member) { $scope.ctrl.member = member; });
}
// we load the availabilities from a callback function of the $scope.eventSources, instead of resolving a promise
// in the router because this allows to refetchEvents from fullCalendar API.
$scope.eventSources.push({
events: function (start, end, timezone, callback) {
Availability.trainings({ trainingId: $stateParams.id }, function (availabilities) {
callback(availabilities);
});
},
textColor: 'black'
});
};
/**
@ -370,7 +395,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
* if it's too late).
* @see http://fullcalendar.io/docs/mouse/eventClick/
*/
var calendarEventClickCb = function (event, jsEvent, view) {
const calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event;
if ($stateParams.id === 'all') {
$scope.training = event.training;
@ -383,7 +408,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element, view) {
const eventRenderCb = function (event, element, view) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = '';
for (let tag of Array.from(event.tags)) {
@ -399,27 +424,29 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
* @param slot {Object}
* @param reservation {Object}
*/
var updateTrainingSlotId = function (slot, reservation) {
const updateTrainingSlotId = function (slot, reservation) {
angular.forEach(reservation.slots, function (s) {
if (slot.start_at === slot.start_at) {
return slot.id = s.id;
if (slot.start.isSame(s.start_at)) {
return slot.slot_id = s.id;
}
});
};
/**
* Update the calendar's display to render the new attributes of the events
* @param events Object[] events to update in full-calendar
*/
var updateCalendar = function () { uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents'); };
const updateEvents = function (...events) {
const realEvents = events.filter(e => !_.isNil(e));
uiCalendarConfig.calendars.calendar.fullCalendar('updateEvents', realEvents);
};
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = function () {
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents');
});
const refetchCalendar = function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
};
// !!! MUST BE CALLED AT THE END of the controller

View File

@ -10,7 +10,7 @@
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'AuthService', 'helpers', '_t',
Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'AuthService', 'helpers', '_t',
function ($rootScope, $uibModal, dialogs, growl, Auth, Price, Wallet, CustomAsset, Slot, AuthService, helpers, _t) {
return ({
restrict: 'E',
@ -28,7 +28,6 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
onSlotAddedToCart: '=',
onSlotRemovedFromCart: '=',
onSlotStartToModify: '=',
onSlotModifyDestination: '=',
onSlotModifySuccess: '=',
onSlotModifyCancel: '=',
onSlotModifyUnselect: '=',
@ -39,7 +38,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
reservableName: '@',
limitToOneSlot: '@'
},
templateUrl: '<%= asset_path "shared/_cart.html" %>',
templateUrl: '/shared/_cart.html',
link ($scope, element, attributes) {
// will store the user's plan if he choosed to buy one
$scope.selectedPlan = null;
@ -78,8 +77,8 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
validateSameTimeReservations(slot, function () {
slot.isValid = true;
updateCartPrice();
})
})
});
});
};
/**
@ -121,15 +120,15 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// first, we ensure that a user was selected (admin/manager) or logged (member)
const isSelectedUser = Object.keys($scope.user).length > 0;
// all slots are in future
const areFutureSlots = _.every($scope.events.reserved, function(s) {
const areFutureSlots = _.every($scope.events.reserved, function (s) {
return s.start.isAfter();
});
if (isSelectedUser && areFutureSlots) {
return $scope.modePlans = true;
} else if (!isSelectedUser){
} else if (!isSelectedUser) {
// otherwise we alert, this error musn't occur when the current user hasn't the admin role
return growl.error(_t('app.shared.cart.please_select_a_member_first'));
} else if (!areFutureSlots){
} else if (!areFutureSlots) {
return growl.error(_t('app.shared.cart.unable_to_select_plan_if_slots_in_the_past'));
}
};
@ -140,7 +139,6 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.payCart = function () {
// first, we check that a user was selected
if (Object.keys($scope.user).length > 0) {
// check selected user has a subscription, if any slot is restricted for subscriptions
const slotValidations = [];
let slotNotValid;
@ -148,16 +146,16 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.events.reserved.forEach(function (slot) {
if (slot.plan_ids.length > 0) {
if (
($scope.selectedPlan && _.include(slot.plan_ids, $scope.selectedPlan.id)) ||
($scope.user.subscribed_plan && _.include(slot.plan_ids, $scope.user.subscribed_plan.id))
($scope.selectedPlan && _.includes(slot.plan_ids, $scope.selectedPlan.id)) ||
($scope.user.subscribed_plan && _.includes(slot.plan_ids, $scope.user.subscribed_plan.id))
) {
slotValidations.push(true);
} else {
slotNotValid = slot;
if ($scope.selectedPlan && !_.include(slot.plan_ids, $scope.selectedPlan.id)) {
if ($scope.selectedPlan && !_.includes(slot.plan_ids, $scope.selectedPlan.id)) {
slotNotValidError = 'selectedPlanError';
}
if ($scope.user.subscribed_plan && !_.include(slot.plan_ids, $scope.user.subscribed_plan.id)) {
if ($scope.user.subscribed_plan && !_.includes(slot.plan_ids, $scope.user.subscribed_plan.id)) {
slotNotValidError = 'userPlanError';
}
if (!$scope.selectedPlan || !$scope.user.subscribed_plan) {
@ -174,15 +172,15 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
} else {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/_reserve_slot_without_plan.html" %>',
templateUrl: '/shared/_reserve_slot_without_plan.html',
size: 'md',
controller: 'ReserveSlotWithoutPlanController',
resolve: {
slot: function() { return slotNotValid; },
slotNotValidError: function() { return slotNotValidError; },
slot: function () { return slotNotValid; },
slotNotValidError: function () { return slotNotValidError; }
}
});
modalInstance.result.then(function(res) {
modalInstance.result.then(function (res) {
return paySlots();
});
}
@ -199,7 +197,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
* When modifying an already booked reservation, confirm the modification.
*/
$scope.modifySlot = function () {
Slot.update({ id: $scope.events.modifiable.id }, {
Slot.update({ id: $scope.events.modifiable.slot_id }, {
slot: {
start_at: $scope.events.placable.start,
end_at: $scope.events.placable.end,
@ -216,23 +214,23 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
};
// -> reset the 'moving' status
$scope.events.placable = null;
return $scope.events.modifiable = null;
$scope.events.modifiable = null;
}
, function (err) { // failure
growl.error(_t('app.shared.cart.unable_to_change_the_reservation'));
return console.error(err);
console.error(err);
});
};
/**
* Cancel the current booking modification, reseting the whole process
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
* @param [event] {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.cancelModifySlot = function (event) {
if (event) { event.preventDefault(); }
if (typeof $scope.onSlotModifyCancel === 'function') { $scope.onSlotModifyCancel(); }
$scope.events.placable = null;
return $scope.events.modifiable = null;
$scope.events.modifiable = null;
};
/**
@ -242,7 +240,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.removeSlotToPlace = function (e) {
e.preventDefault();
if (typeof $scope.onSlotModifyUnselect === 'function') { $scope.onSlotModifyUnselect(); }
return $scope.events.placable = null;
$scope.events.placable = null;
};
/**
@ -251,7 +249,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
*/
$scope.tagMissmatch = function () {
if ($scope.events.placable.tag_ids.length === 0) { return false; }
for (let tag of Array.from($scope.events.modifiable.tags)) {
for (const tag of Array.from($scope.events.modifiable.tags)) {
if (!Array.from($scope.events.placable.tag_ids).includes(tag.id)) {
return true;
}
@ -271,7 +269,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
return false;
}
};
/* PRIVATE SCOPE */
@ -318,20 +316,20 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// ask confirmation
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/_reserve_slot_tags_mismatch.html" %>',
templateUrl: '/shared/_reserve_slot_tags_mismatch.html',
size: 'md',
controller: 'ReserveSlotTagsMismatchController',
resolve: {
slotTags: function() { return slot.tags; },
slotTags: function () { return slot.tags; },
userTags: function () { return $scope.user.tags; },
userName: function () { return $scope.user.name; }
}
});
modalInstance.result.then(function(res) {
modalInstance.result.then(function (res) {
if (typeof callback === 'function') callback(res);
});
}
}
};
/**
* Validates that no other reservations were made that conflict the current slot and alert the user about the conflict.
@ -346,32 +344,32 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
'space_reservations',
'events_reservations'
].map(function (k) {
return _.filter($scope.user[k], function(r) {
return _.filter($scope.user[k], function (r) {
return slot.start.isSame(r.start_at) ||
(slot.end.isAfter(r.start_at) && slot.end.isBefore(r.end_at)) ||
(slot.start.isAfter(r.start_at) && slot.start.isBefore(r.end_at)) ||
(slot.start.isBefore(r.start_at) && slot.end.isAfter(r.end_at));
})
});
});
sameTimeReservations = _.union.apply(null, sameTimeReservations);
if (sameTimeReservations.length > 0) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "shared/_reserve_slot_same_time.html" %>',
templateUrl: '/shared/_reserve_slot_same_time.html',
size: 'md',
controller: 'ReserveSlotSameTimeController',
resolve: {
sameTimeReservations: function() { return sameTimeReservations; },
sameTimeReservations: function () { return sameTimeReservations; },
bookOverlappingSlotsPromise: ['Setting', function (Setting) { return Setting.get({ name: 'book_overlapping_slots' }).$promise; }]
}
});
modalInstance.result.then(function(res) {
modalInstance.result.then(function (res) {
if (typeof callback === 'function') callback(res);
});
} else {
if (typeof callback === 'function') callback();
}
}
};
/**
* Callback triggered when the selected slot changed
@ -381,13 +379,13 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// if this slot is restricted for subscribers...
if ($scope.slot.plan_ids.length > 0) {
// ... we select all the plans matching these restrictions...
const _plans = _.filter($scope.plans, function (p) { return _.include($scope.slot.plan_ids, p.id) });
const _plans = _.filter($scope.plans, function (p) { return _.includes($scope.slot.plan_ids, p.id); });
// ... and we group these plans, by Group...
$scope.slot.plansGrouped = [];
$scope.slot.group_ids = [];
for (let group of Array.from($scope.groups)) {
for (const group of Array.from($scope.groups)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (let plan of Array.from(_plans)) {
for (const plan of Array.from(_plans)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
@ -400,13 +398,13 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
}
$scope.slot.group_ids = $scope.slot.plansGrouped.map(function(g) { return g.id; });
$scope.slot.group_ids = $scope.slot.plansGrouped.map(function (g) { return g.id; });
}
if (!$scope.slot.is_reserved && !$scope.events.modifiable && !$scope.slot.is_completed) {
// slot is not reserved and we are not currently modifying a slot
// -> can be added to cart or removed if already present
const index = $scope.events.reserved.indexOf($scope.slot);
const index = _.findIndex($scope.events.reserved, (e) => e._id === $scope.slot._id);
if (index === -1) {
if (($scope.limitToOneSlot === 'true') && $scope.events.reserved[0]) {
// if we limit the number of slots in the cart to 1, and there is already
@ -427,7 +425,11 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
} else if (!$scope.slot.is_reserved && !$scope.slot.is_completed && $scope.events.modifiable) {
// slot is not reserved but we are currently modifying a slot
// -> we request the calender to change the rendering
if (typeof $scope.onSlotModifyUnselect === 'function') { $scope.onSlotModifyUnselect(); }
if (typeof $scope.onSlotModifyUnselect === 'function') {
// if the callback return false, cancel the selection for the current modification
const res = $scope.onSlotModifyUnselect();
if (!res) return;
}
// -> then, we re-affect the destination slot
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.slot._id)) {
return $scope.events.placable = $scope.slot;
@ -437,17 +439,17 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
} else if ($scope.slot.is_reserved && $scope.events.modifiable && ($scope.slot.is_reserved._id === $scope.events.modifiable._id)) {
// slot is reserved and currently modified
// -> we cancel the modification
return $scope.cancelModifySlot();
$scope.cancelModifySlot();
} else if ($scope.slot.is_reserved && (slotCanBeModified($scope.slot) || slotCanBeCanceled($scope.slot)) && !$scope.events.modifiable && ($scope.events.reserved.length === 0)) {
// slot is reserved and is ok to be modified or cancelled
// but we are not currently running a modification or having any slots in the cart
// -> first the affect the modification/cancellation rights attributes to the current slot
// -> first affect the modification/cancellation rights attributes to the current slot
resetCartState();
$scope.slot.movable = slotCanBeModified($scope.slot);
$scope.slot.cancelable = slotCanBeCanceled($scope.slot);
// -> then, we open a dialog to ask to the user to choose an action
return dialogs.confirm({
templateUrl: '<%= asset_path "shared/confirm_modify_slot_modal.html" %>',
templateUrl: '/shared/confirm_modify_slot_modal.html',
resolve: {
object () { return $scope.slot; }
}
@ -456,9 +458,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// the user has chosen an action, so we proceed
if (type === 'move') {
if (typeof $scope.onSlotStartToModify === 'function') { $scope.onSlotStartToModify(); }
return $scope.events.modifiable = $scope.slot;
$scope.events.modifiable = $scope.slot;
} else if (type === 'cancel') {
return dialogs.confirm(
dialogs.confirm(
{
resolve: {
object () {
@ -470,7 +472,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
},
function () { // cancel confirmed
Slot.cancel({ id: $scope.slot.id }, function () { // successfully canceled
Slot.cancel({ id: $scope.slot.slot_id }, function () { // successfully canceled
growl.success(_t('app.shared.cart.reservation_was_cancelled_successfully'));
if (typeof $scope.onSlotCancelSuccess === 'function') { return $scope.onSlotCancelSuccess(); }
}
@ -494,7 +496,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.events.moved = null;
$scope.events.paid = [];
$scope.events.modifiable = null;
return $scope.events.placable = null;
$scope.events.placable = null;
};
/**
@ -612,7 +614,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
*/
const payByStripe = function (reservation) {
$uibModal.open({
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
templateUrl: '/stripe/payment_modal.html',
size: 'md',
resolve: {
reservation () {
@ -666,14 +668,14 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
};
}
]
}).result['finally'](null).then(function (reservation) { afterPayment(reservation); });
}).result.finally(null).then(function (reservation) { afterPayment(reservation); });
};
/**
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
*/
const payOnSite = function (reservation) {
$uibModal.open({
templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>',
templateUrl: '/shared/valid_reservation_modal.html',
size: 'sm',
resolve: {
reservation () {
@ -735,7 +737,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
}
]
}).result['finally'](null).then(function (reservation) { afterPayment(reservation); });
}).result.finally(null).then(function (reservation) { afterPayment(reservation); });
};
/**
* Actions to run after the payment was successful
@ -756,22 +758,22 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
/**
* Actions to pay slots
*/
const paySlots = function() {
const paySlots = function () {
const reservation = mkReservation($scope.user, $scope.events.reserved, $scope.selectedPlan);
return Wallet.getWalletByUser({ user_id: $scope.user.id }, function (wallet) {
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
if ((AuthService.isAuthorized(['member']) && amountToPay > 0)
|| (AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
if ((AuthService.isAuthorized(['member']) && amountToPay > 0) ||
(AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
if ($scope.settings.online_payment_module !== 'true') {
growl.error(_t('app.shared.cart.online_payment_disabled'));
} else {
return payByStripe(reservation);
}
} else {
if (AuthService.isAuthorized(['admin'])
|| (AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id)
|| amountToPay === 0) {
if (AuthService.isAuthorized(['admin']) ||
(AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id) ||
amountToPay === 0) {
return payOnSite(reservation);
}
}
@ -798,17 +800,16 @@ Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '
*/
$scope.ok = function () {
$uibModalInstance.close({});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);
/**
* Controller of the modal showing the slot tags
*/
@ -823,13 +824,13 @@ Application.Controllers.controller('ReserveSlotTagsMismatchController', ['$scope
*/
$scope.ok = function () {
$uibModalInstance.close({});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);
@ -845,12 +846,12 @@ Application.Controllers.controller('ReserveSlotWithoutPlanController', ['$scope'
*/
$scope.ok = function () {
$uibModalInstance.close({});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);

View File

@ -18,7 +18,7 @@ Application.Directives.directive('coupon', [ '$rootScope', 'Coupon', '_t', funct
total: '=',
userId: '@'
},
templateUrl: '<%= asset_path "shared/_coupon.html" %>',
templateUrl: '/shared/_coupon.html',
link ($scope, element, attributes) {
// Whether code input is shown or not (ie. the link 'I have a coupon' is shown)
$scope.code =

View File

@ -15,6 +15,6 @@ Application.Directives.directive('fabUserAvatar', [ function () {
userAvatar: '=ngModel',
avatarClass: '@'
},
templateUrl: '<%= asset_path "shared/_user_avatar.html" %>'
templateUrl: '/shared/_user_avatar.html'
});
}]);

View File

@ -2,7 +2,7 @@ Application.Directives.directive('events', [ 'Event',
function (Event) {
return ({
restrict: 'E',
templateUrl: '<%= asset_path "home/events.html" %>',
templateUrl: '/home/events.html',
link ($scope, element, attributes) {
// The closest upcoming events
$scope.upcomingEvents = null;

View File

@ -2,7 +2,7 @@ Application.Directives.directive('news', [ 'Setting',
function (Setting) {
return ({
restrict: 'E',
templateUrl: '<%= asset_path "home/news.html" %>',
templateUrl: '/home/news.html',
link ($scope, element, attributes) {
// The admin blogpost
$scope.homeBlogpost = null;

View File

@ -2,7 +2,7 @@ Application.Directives.directive('projects', [ 'Project',
function (Project) {
return ({
restrict: 'E',
templateUrl: '<%= asset_path "home/projects.html" %>',
templateUrl: '/home/projects.html',
link ($scope, element, attributes) {
// The last projects published/documented on the platform
$scope.lastProjects = null;

View File

@ -8,7 +8,7 @@ Application.Directives.directive('twitter', ['Setting',
function (Setting) {
return ({
restrict: 'E',
templateUrl: '<%= asset_path "home/twitter.html" %>',
templateUrl: '/home/twitter.html',
link ($scope, element, attributes) {
// Twitter username
$scope.twitterName = null;

View File

@ -2,7 +2,7 @@ Application.Directives.directive('members', [ 'Member',
function (Member) {
return ({
restrict: 'E',
templateUrl: '<%= asset_path "home/members.html" %>',
templateUrl: '/home/members.html',
link ($scope, element, attributes) {
// The last registered members who confirmed their addresses
$scope.lastMembers = null;

View File

@ -11,7 +11,7 @@
Application.Directives.directive('selectMember', [ 'Diacritics', 'Member', function (Diacritics, Member) {
return ({
restrict: 'E',
templateUrl: '<%= asset_path "shared/_member_select.html" %>',
templateUrl: '/shared/_member_select.html',
link (scope, element, attributes) {
return scope.autoCompleteName = function (nameLookup) {
if (!nameLookup) {

View File

@ -11,7 +11,7 @@ Application.Directives.directive('booleanSetting', ['Setting', 'growl', '_t',
classes: '@',
onBeforeSave: '='
},
templateUrl: '<%= asset_path "admin/settings/boolean.html" %>',
templateUrl: '/admin/settings/boolean.html',
link ($scope, element, attributes) {
// The setting
$scope.setting = {
@ -20,8 +20,8 @@ Application.Directives.directive('booleanSetting', ['Setting', 'growl', '_t',
};
// default values for the switch labels
$scope.yesLabel = $scope.yesLabel || 'app.admin.settings.enabled';
$scope.noLabel = $scope.noLabel || 'app.admin.settings.disabled';
$scope.yesLabel = $scope.yesLabel || 'app.shared.buttons.yes';
$scope.noLabel = $scope.noLabel || 'app.shared.buttons.no';
/**
* Callback to save the setting value to the database

View File

@ -12,7 +12,7 @@ Application.Directives.directive('numberSetting', ['Setting', 'growl', '_t',
min: '@',
required: '<'
},
templateUrl: '<%= asset_path "admin/settings/number.html" %>',
templateUrl: '/admin/settings/number.html',
link ($scope, element, attributes) {
// The setting
$scope.setting = {

View File

@ -12,7 +12,7 @@ Application.Directives.directive('selectMultipleSetting', ['Setting', 'growl', '
descriptionNew: '@',
beforeAdd: '='
},
templateUrl: '<%= asset_path "admin/settings/select-multiple.html" %>',
templateUrl: '/admin/settings/select-multiple.html',
link ($scope, element, attributes) {
// The setting
$scope.setting = {
@ -29,22 +29,22 @@ Application.Directives.directive('selectMultipleSetting', ['Setting', 'growl', '
/**
* Remove the items in the selection from the options and update setting.value
*/
$scope.removeItem = function() {
$scope.removeItem = function () {
const options = $scope.options.filter(function (opt) {
return $scope.selection.indexOf(opt) < 0;
})
});
$scope.options = options;
$scope.setting.value = options.join(' ');
growl.success(_t('app.admin.settings.COUNT_items_removed', { COUNT: $scope.selection.length }));
$scope.selection = [];
}
};
/**
* Open a modal dialog asking for the value of a new item to add
*/
$scope.addItem = function() {
$scope.addItem = function () {
$uibModal.open({
templateUrl: 'newSelectOption.html',
templateUrl: '/admin/settings/newSelectOption.html',
resolve: {
titleNew: function () { return $scope.titleNew; },
descriptionNew: function () { return $scope.descriptionNew; }
@ -60,7 +60,7 @@ Application.Directives.directive('selectMultipleSetting', ['Setting', 'growl', '
$uibModalInstance.dismiss('cancel');
};
}
}).result['finally'](null).then(function(val) {
}).result.finally(null).then(function (val) {
const options = Array.from($scope.options);
if (typeof $scope.beforeAdd === 'function') { val = $scope.beforeAdd(val); }
options.push(val);
@ -68,14 +68,14 @@ Application.Directives.directive('selectMultipleSetting', ['Setting', 'growl', '
$scope.setting.value = options.join(' ');
growl.success(_t('app.admin.settings.item_added'));
});
}
};
/**
* Callback to save the setting value to the database
* @param setting {{value:*, name:string}} note that the value will be stringified
*/
$scope.save = function (setting) {
let { value } = setting;
const { value } = setting;
Setting.update(
{ name: setting.name },

View File

@ -14,7 +14,7 @@ Application.Directives.directive('selectSetting', ['Setting', 'growl', '_t',
option4: '<',
option5: '<'
},
templateUrl: '<%= asset_path "admin/settings/select.html" %>',
templateUrl: '/admin/settings/select.html',
link ($scope, element, attributes) {
// The setting
$scope.setting = {

View File

@ -15,7 +15,7 @@ Application.Directives.directive('textSetting', ['Setting', 'growl', '_t',
minLength: '@',
readOnly: '<'
},
templateUrl: '<%= asset_path "admin/settings/text.html" %>',
templateUrl: '/admin/settings/text.html',
link ($scope, element, attributes) {
// if type is not specified, use text as default
if (typeof $scope.type === 'undefined') {

View File

@ -16,13 +16,13 @@ Application.Directives.directive('socialLink', [ function () {
network: '@?',
user: '='
},
templateUrl: '<%= asset_path "shared/_social_link.html" %>',
templateUrl: '/shared/_social_link.html',
link (scope, element, attributes) {
if (scope.network === 'dailymotion') {
scope.image = "<%= asset_path('social/dailymotion.png') %>";
scope.image = "social/dailymotion.png";
return scope.altText = 'd';
} else if (scope.network === 'echosciences') {
scope.image = "<%= asset_path('social/echosciences.png') %>";
scope.image = "social/echosciences.png";
return scope.altText = 'E)';
} else {
if (scope.network === 'website') {

Some files were not shown because too many files have changed in this diff Show More