1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-17 06:52:27 +01:00

Merge branch 'dev' into dependabot/bundler/puma-3.12.3

This commit is contained in:
Sylvain 2020-03-02 08:33:42 +01:00 committed by GitHub
commit cd61826b6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
337 changed files with 12998 additions and 7935 deletions

View File

@ -1,8 +1,14 @@
This issue tracker is **reserved** for bug reports and feature requests.
This issue tracker is **reserved** for bug reports.
The place to ask a question or call for help is at Fab-manager forums at https://forum.fab-manager.com/.
The place to ask a question or call for help is at [Fab-manager forums](https://forum.fab-manager.com)
The place to request or vote for new feature is on the [feedback website](https://feedback.fab-manager.com)
To report a bug, please describe:
- Expected behavior and actual behavior.
- Steps to reproduce the problem.
- Specifications like the version of the project, operating system, or hardware.
The following elements may help to quickly resolve your issue:
- Server logs `tail -f /apps/fabmanager/log/app-stdout.log` on the server
- Client logs `Ctrl`+`Maj`+`i` > `Console` in the browser

View File

@ -1,5 +1,39 @@
# Changelog Fab Manager
- Ability to create and delete periodic calendar availabilities (recurrence)
- An administrator can delete a member
- An event can be cancelled, if reservation cancellation is enabled
- Ability to import iCalendar agendas in the public calendar, through URLs to ICS files (RFC 5545)
- Ability to configure the duration of a reservation slot, using `SLOT_DURATION`. Previously, only 60 minutes slots were allowed
- Display the scheduled events in the admin calendar, depending on `EVENTS_IN_CALENDAR` configuration.
- Display indications on required fields in new administrator form
- Configuration of phone number in members registration forms: can be required or optional, depending on `PHONE_REQUIRED` configuration
- Improved user experience in defining slots in the calendar management
- Improved notification email to the member when a rolling subscription is taken
- Notify all admins on the creation of a refund invoice
- Calendar management: improved legend display and visual behavior
- Prevent event reservation in the past [Taiga#127]
- Handle Ctrl^C in upgrade scripts
- Updated moment-timezone
- Added freeCAD files as default allowed extensions
- Rake task to sync local users with Stripe
- Unified translations syntax to use ICU MessageFormat
- Refactored front-end translations keys with unified paths
- Updated and refactored README and documentations
- Updated setup script and instructions
- Fix a bug: unable to remove the picture from a training
- Fix a bug: no alerts on errors during admin creation
- Fix a bug: replaces all Time.now by DateTime.current to prevent time zones issues [Taiga#134]
- Fix a bug: logs are not printed in staging environment
- Fix a security issue: updated loofah to fix [CVE-2019-15587](https://github.com/advisories/GHSA-c3gv-9cxf-6f57)
- Fix a security issue: updated angular to 1.7.9 to fix [CVE-2019-10768](https://github.com/advisories/GHSA-89mq-4x47-5v83)
- Fix a security issue: updated puma to 3.12.2 to fix [GHSA-7xx3-m584-x994](https://github.com/advisories/GHSA-7xx3-m584-x994)
- [TODO DEPLOY] add the `SLOT_DURATION` environment variable (see [doc/environment.md](doc/environment.md#SLOT_DURATION) for configuration details)
- [TODO DEPLOY] add the `PHONE_REQUIRED` environment variable (see [doc/environment.md](doc/environment.md#PHONE_REQUIRED) for configuration details)
- [TODO DEPLOY] add the `EVENTS_IN_CALENDAR` environment variable (see [doc/environment.md](doc/environment.md#EVENTS_IN_CALENDAR) for configuration details)
- [TODO DEPLOY] -> (only dev) `bundle install && yarn install`
- [TODO DEPLOY] `rake db:migrate`
## v4.2.4 2019 October 30
- Fix a bug: in some cases, the invoices were not generated after deploying v4.2.0+. This can occurs if VAT was changed/enabled during the application life (#156)

View File

@ -61,7 +61,7 @@ group :test do
gem 'webmock'
end
group :production do
group :production, :staging do
gem 'rails_12factor'
end
@ -152,3 +152,5 @@ gem 'sys-filesystem'
gem 'sha3'
gem 'repost'
gem 'icalendar'

View File

@ -191,6 +191,9 @@ GEM
multi_xml (>= 0.5.2)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
icalendar (2.5.3)
ice_cube (~> 0.16)
ice_cube (0.16.3)
ice_nine (0.11.2)
jaro_winkler (1.5.1)
jbuilder (2.5.0)
@ -208,7 +211,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
libv8 (3.16.14.19)
loofah (2.3.0)
loofah (2.3.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@ -240,7 +243,7 @@ GEM
multi_xml (0.6.0)
multipart-post (2.1.1)
naught (1.1.0)
nokogiri (1.10.4)
nokogiri (1.10.8)
mini_portile2 (~> 2.4.0)
notify_with (0.0.2)
jbuilder (~> 2.0)
@ -288,7 +291,7 @@ GEM
pundit (1.0.0)
activesupport (>= 3.0.0)
raabro (1.1.6)
rack (1.6.11)
rack (1.6.12)
rack-protection (1.5.5)
rack
rack-test (0.6.3)
@ -497,6 +500,7 @@ DEPENDENCIES
forgery
friendly_id (~> 5.1.0)
has_secure_token
icalendar
jbuilder (~> 2.5)
jbuilder_cache_multi
jquery-rails

355
README.md
View File

@ -5,31 +5,19 @@ FabManager is the Fab Lab management solution. It provides a comprehensive, web-
[![Coverage Status](https://coveralls.io/repos/github/sleede/fab-manager/badge.svg)](https://coveralls.io/github/sleede/fab-manager)
[![Docker pulls](https://img.shields.io/docker/pulls/sleede/fab-manager.svg)](https://hub.docker.com/r/sleede/fab-manager/)
[![Docker Build Status](https://img.shields.io/docker/build/sleede/fab-manager.svg)](https://hub.docker.com/r/sleede/fab-manager/builds)
[![Crowdin](https://badges.crowdin.net/fab-manager/localized.svg)](https://crowdin.com/project/fab-manager)
##### Table of Contents
1. [Software stack](#software-stack)
2. [Contributing](#contributing)
3. [Setup a production environment](#setup-a-production-environment)
4. [Setup a development environment](#setup-a-development-environment)<br/>
4.1. [General Guidelines](#general-guidelines)<br/>
5. [PostgreSQL](#postgresql)<br/>
5.1. [Install PostgreSQL 9.6](#setup-postgresql)
6. [ElasticSearch](#elasticsearch)<br/>
6.1. [Install ElasticSearch](#setup-elasticsearch)<br/>
6.2. [Rebuild statistics](#rebuild-stats)<br/>
6.3. [Backup and Restore](#backup-and-restore-elasticsearch)
7. [Internationalization (i18n)](#i18n)<br/>
7.1. [Translation](#i18n-translation)<br/>
7.1.1. [Front-end translations](#i18n-translation-front)<br/>
7.1.2. [Back-end translations](#i18n-translation-back)<br/>
7.2. [Configuration](#i18n-configuration)<br/>
7.2.1. [Settings](#i18n-settings)<br/>
7.2.2. [Applying changes](#i18n-apply)
8. [Open Projects](#open-projects)
9. [Plugins](#plugins)
10. [Single Sign-On](#sso)
11. [Known issues](#known-issues)
12. [Related Documentation](#related-documentation)
4. [Setup a development environment](#setup-a-development-environment)
5. [Internationalization (i18n)](#i18n)
6. [Open Projects](#open-projects)
7. [Plugins](#plugins)
8. [Single Sign-On](#sso)
9. [Known issues](#known-issues)
10. [Related Documentation](#related-documentation)
@ -54,293 +42,22 @@ Contributions are welcome. Please read [the contribution guidelines](CONTRIBUTIN
## Setup a production environment
To run fab-manager as a production application, this is highly recommended to use [Docker-compose](https://docs.docker.com/compose/overview/).
The procedure to follow is described in the [docker-compose readme](docker/README.md).
The procedure to follow is described in the [docker-compose readme](doc/docker-compose_readme.md).
<a name="setup-a-development-environment"></a>
## Setup a development environment
In you intend to run fab-manager on your local machine to contribute to the project development, you can set it up with the following procedure.
In you intend to run fab-manager on your local machine to contribute to the project development, you can set it up by following the [development readme](doc/development_readme.md).
This procedure relies on docker to set-up the dependencies.
This procedure is not easy to follow so if you don't need to write some code for Fab-manager, please prefer the [docker-compose installation method](docker/README.md).
Optionally, you can use a virtual development environment that relies on Vagrant and Virtual Box by following the [virtual machine instructions](doc/virtual-machine.md).
<a name="general-guidelines"></a>
### General Guidelines
1. Install RVM, with the ruby version specified in the [.ruby-version file](.ruby-version).
For more details about the process, please read the [official RVM documentation](http://rvm.io/rvm/install).
If you're using ArchLinux, you may have to [read this](doc/archlinux_readme.md) before.
2. Install NVM, with the node.js version specified in the [.nvmrc file](.nvmrc).
For instructions about installing NVM, please refer to [the NVM readme](https://github.com/creationix/nvm#installation).
3. Install Yarn, the front-end package manager.
Depending on your system, the installation process may differ, please read the [official Yarn documentation](https://yarnpkg.com/en/docs/install#debian-stable).
4. Install docker.
Your system may provide a pre-packaged version of docker in its repositories, but this version may be outdated.
Please refer to [ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/), [debian](https://docs.docker.com/install/linux/docker-ce/debian/) or [MacOS](https://docs.docker.com/docker-for-mac/install/) documentation to setup a recent version of docker.
5. Add your current user to the docker group, to allow using docker without `sudo`.
```bash
# add the docker group if it doesn't already exist
sudo groupadd docker
# add the current user to the docker group
sudo usermod -aG docker $(whoami)
# restart to validate changes
sudo reboot
```
6. Create a docker network for fab-manager.
You may have to change the network address if it is already in use.
```bash
docker network create --subnet=172.18.0.0/16 fabmanager
```
7. Retrieve the project from Git
```bash
git clone https://github.com/sleede/fab-manager.git
```
8. Install the software dependencies.
First install [PostgreSQL](#postgresql) and [ElasticSearch](#elasticsearch) as specified in their respective documentations.
Then install the other dependencies:
- For Ubuntu/Debian:
```bash
# on Ubuntu 18.04 server, you may have to enable the "universe" repository
sudo add-apt-repository universe
# then, install the dependencies
sudo apt-get install libpq-dev redis-server imagemagick
```
- For MacOS X:
```bash
brew install redis imagemagick
```
9. Init the RVM and NVM instances and check they were correctly configured
```bash
cd fab-manager
rvm current | grep -q `cat .ruby-version`@fab-manager && echo "ok"
# Must print ok
nvm use
node --version | grep -q `cat .nvmrc` && echo "ok"
# Must print ok
```
10. Install bundler in the current RVM gemset
```bash
gem install bundler --version=1.17.3
```
11. Install the required ruby gems and javascript plugins
```bash
bundle install
yarn install
```
12. Create the default configuration files **and configure them!** (see the [environment configuration documentation](doc/environment.md))
```bash
cp config/database.yml.default config/database.yml
cp config/application.yml.default config/application.yml
vi config/application.yml
# or use your favorite text editor instead of vi (nano, ne...)
```
13. Build the databases.
- **Warning**: **DO NOT** run `rake db:setup` instead of these commands, as this will not run some required raw SQL instructions.
- **Please note**: Your password length must be between 8 and 128 characters, otherwise db:seed will be rejected. This is configured in [config/initializers/devise.rb](config/initializers/devise.rb)
```bash
# for dev
rake db:create
rake db:migrate
ADMIN_EMAIL='youradminemail' ADMIN_PASSWORD='youradminpassword' rake db:seed
rake fablab:es:build_stats
# for tests
RAILS_ENV=test rake db:create
RAILS_ENV=test rake db:migrate
```
14. Create the pids folder used by Sidekiq. If you want to use a different location, you can configure it in `config/sidekiq.yml`
```bash
mkdir -p tmp/pids
```
15. Start the development web server
```bash
foreman s -p 3000
```
16. You should now be able to access your local development FabManager instance by accessing `http://localhost:3000` in your web browser.
17. You can login as the default administrator using the credentials defined previously.
18. Email notifications will be caught by MailCatcher.
To see the emails sent by the platform, open your web browser at `http://localhost:1080` to access the MailCatcher interface.
<a name="postgresql"></a>
## PostgreSQL
<a name="setup-postgresql"></a>
### Install PostgreSQL 9.6
We will use docker to easily install the required version of PostgreSQL.
1. Create the docker binding folder
```bash
mkdir -p .docker/postgresql
```
2. Start the PostgreSQL container.
```bash
docker run --restart=always -d --name fabmanager-postgres \
-v $(pwd)/.docker/postgresql:/var/lib/postgresql/data \
--network fabmanager --ip 172.18.0.2 \
-p 5432:5432 \
postgres:9.6
```
3. Configure fab-manager to use it.
On linux systems, PostgreSQL will be available at 172.18.0.2.
On MacOS, you'll have to set the host to 127.0.0.1 (or localhost).
See [environment.md](doc/environment.md) for more details.
4 . Finally, you may want to have a look at detailed informations about PostgreSQL usage in fab-manager.
Some information about that is available in the [PostgreSQL Readme](doc/postgresql_readme.md).
<a name="elasticsearch"></a>
## ElasticSearch
ElasticSearch is a powerful search engine based on Apache Lucene combined with a NoSQL database used as a cache to index data and quickly process complex requests on it.
In FabManager, it is used for the admin's statistics module and to perform searches in projects.
<a name="setup-elasticsearch"></a>
### Install ElasticSearch
1. Create the docker binding folders
```bash
mkdir -p .docker/elasticsearch/config
mkdir -p .docker/elasticsearch/plugins
mkdir -p .docker/elasticsearch/backups
```
2. Copy the default configuration files
```bash
cp docker/elasticsearch.yml .docker/elasticsearch/config
cp docker/log4j2.properties .docker/elasticsearch/config
```
3. Start the ElasticSearch container.
```bash
docker run --restart=always -d --name fabmanager-elastic \
-v $(pwd)/.docker/elasticsearch/config:/usr/share/elasticsearch/config \
-v $(pwd)/.docker/elasticsearch:/usr/share/elasticsearch/data \
-v $(pwd)/.docker/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-v $(pwd)/.docker/elasticsearch/backups:/usr/share/elasticsearch/backups \
--network fabmanager --ip 172.18.0.3 \
-p 9200:9200 -p 9300:9300 \
elasticsearch:5.6
```
4. Configure fab-manager to use it.
On linux systems, ElasticSearch will be available at 172.18.0.3.
On MacOS, you'll have to set the host to 127.0.0.1 (or localhost).
See [environment.md](doc/environment.md) for more details.
<a name="rebuild-stats"></a>
### Rebuild statistics
Every nights, the statistics for the day that just ended are built automatically at 01:00 (AM) and stored in ElastricSearch.
See [schedule.yml](config/schedule.yml) to modify this behavior.
If the scheduled task wasn't executed for any reason (eg. you are in a dev environment and your computer was turned off at 1 AM), you can force the statistics data generation in ElasticSearch, running the following command.
```bash
# Here for the 50 last days
rake fablab:es:generate_stats[50]
```
<a name="backup-and-restore-elasticsearch"></a>
### Backup and Restore
To backup and restore the ElasticSearch database, use the [elasticsearch-dump](https://github.com/taskrabbit/elasticsearch-dump) tool.
Dump the database with: `elasticdump --input=http://localhost:9200/stats --output=fablab_stats.json`.
Restore it with: `elasticdump --input=fablab_stats.json --output=http://localhost:9200/stats`.
Optionally, you can use a virtual development environment that relies on Vagrant and Virtual Box by following the [virtual machine instructions](virtual-machine.md).
<a name="i18n"></a>
## Internationalization (i18n)
The FabManager application can only run in a single language but this language can easily be changed.
<a name="i18n-translation"></a>
### Translation
Check the files located in `config/locales`:
- Front app translations (angular.js) are located in `config/locales/app.scope.XX.yml`.
Where scope has one the following meaning :
- admin: translations of the administrator views (manage and configure the FabLab).
- logged: translations of the end-user's views accessible only to connected users.
- public: translation of end-user's views publicly accessible to anyone.
- shared: translations shared by many views (like forms or buttons).
- Back app translations (Ruby on Rails) are located in `config/locales/XX.yml`.
- Emails translations are located in `config/locales/mails.XX.yml`.
- Messages related to the authentication system are located in `config/locales/devise.XX.yml`.
If you plan to translate the application to a new locale, please consider that the reference translation is French.
Indeed, in some cases, the English texts/sentences can seems confuse or lack of context as they were originally translated from French.
To prevent syntax mistakes while translating locale files, we **STRONGLY advise** you to use a text editor which support syntax coloration for YML and Ruby.
<a name="i18n-translation-front"></a>
#### Front-end translations
Front-end translations uses [angular-translate](http://angular-translate.github.io) with some interpolations interpreted by angular.js and other interpreted by [MessageFormat](https://github.com/SlexAxton/messageformat.js/).
**These two kinds of interpolation use a near but different syntax witch SHOULD NOT be confused.**
Please refer to the official [angular-translate documentation](http://angular-translate.github.io/docs/#/guide/14_pluralization) before translating.
<a name="i18n-translation-back"></a>
#### Back-end translations
Back-end translations uses the [Ruby on Rails syntax](http://guides.rubyonrails.org/i18n.html) but some complex interpolations are interpreted by [MessageFormat](https://github.com/format-message/message-format-rb) and are marked as it in comments.
**DO NOT confuse the syntaxes.**
In each cases, some inline comments are included in the localisation files.
They can be recognized as they start with the sharp character (#).
These comments are not required to be translated, they are intended to help the translator to have some context information about the sentence to translate.
You will also need to translate the invoice watermark, located in `app/pdfs/data/`.
You'll find there the [GIMP source of the image](app/pdfs/data/watermark.xcf), which is using [Rubik Mono One](https://fonts.google.com/specimen/Rubik+Mono+One) as font.
Use it to generate a similar localised PNG image which keep the default image size, as PDF are not responsive.
<a name="i18n-configuration"></a>
### Configuration
Locales configurations are made in `config/application.yml`.
If you are in a development environment, your can keep the default values, otherwise, in production, values must be configured carefully.
<a name="i18n-settings"></a>
#### Settings
Please refer to the [environment configuration documentation](doc/environment.md#internationalization-settings)
<a name="i18n-apply"></a>
#### Applying changes
After modifying any values concerning the localisation, restart the application (ie. web server) to apply these changes in the i18n configuration.
Please refer to the [translation readme](doc/translation_readme.md) for instructions about configuring the language or to contribute to the translation.
<a name="open-projects"></a>
## Open Projects
@ -390,51 +107,7 @@ Developers may find information on how to implement their own authentication pro
<a name="known-issues"></a>
## Known issues
- When browsing a machine page, you may encounter an "InterceptError" in the console and the loading bar will stop loading before reaching its ending.
This may happen if the machine was created through a seed file without any image.
To solve this, simply add an image to the machine's profile and refresh the web page.
- When starting the Ruby on Rails server (eg. `foreman s`) you may receive the following error:
worker.1 | invalid url: redis::6379
web.1 | Exiting
worker.1 | ...lib/redis/client.rb...:in `_parse_options'
This may happen when the `application.yml` file is missing.
To solve this issue copy `config/application.yml.default` to `config/application.yml`.
This is required before the first start.
- Due to a stripe limitation, you won't be able to create plans longer than one year.
- When running the tests suite with `rake test`, all tests may fail with errors similar to the following:
Error:
...
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "..." violates foreign key constraint "fk_rails_..."
DETAIL: Key (group_id)=(1) is not present in table "...".
: ...
test_after_commit (1.0.0) lib/test_after_commit/database_statements.rb:11:in `block in transaction'
test_after_commit (1.0.0) lib/test_after_commit/database_statements.rb:5:in `transaction'
This is due to an ActiveRecord behavior witch disable referential integrity in PostgreSQL to load the fixtures.
PostgreSQL will prevent any users to disable referential integrity on the fly if they doesn't have the `SUPERUSER` role.
To fix that, logon as the `postgres` user and run the PostgreSQL shell (see [the dedicated section](#run-postgresql-cli) for instructions).
Then, run the following command (replace `sleede` with your test database user, as specified in your database.yml):
ALTER ROLE sleede WITH SUPERUSER;
DO NOT do this in a production environment, unless you know what you're doing: this could lead to a serious security issue.
- With Ubuntu 16.04, ElasticSearch may refuse to start even after having configured the service with systemd.
To solve this issue, you may have to set `START_DAEMON` to `true` in `/etc/default/elasticsearch`.
Then reload ElasticSearch with:
```bash
sudo systemctl restart elasticsearch.service
```
- In some cases, the invoices won't be generated. This can be due to the image included in the invoice header not being supported.
To fix this issue, change the image in the administrator interface (manage the invoices / invoices settings).
See [this thread](https://forum.fab-manager.com/t/resolu-erreur-generation-facture/428) for more info.
Before reporting an issue, please check if your issue is not listed in the [know issues](doc/known-issues.md) with its solution.
<a name="related-documentation"></a>
## Related Documentation

View File

@ -59,8 +59,8 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
$translateProvider.useLoaderCache(true);
// Secure i18n module against XSS attacks by escaping the output
$translateProvider.useSanitizeValueStrategy('escapeParameters');
// Enable the MessageFormat interpolation (used for pluralization)
$translateProvider.addInterpolation('$translateMessageFormatInterpolation');
// Use the MessageFormat interpolation by default (used for pluralization)
$translateProvider.useMessageFormatInterpolation();
// Set the langage of the instance (from ruby configuration)
$translateProvider.preferredLanguage(Fablab.locale);
}]).run(['$rootScope', '$log', 'AuthService', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics',
@ -86,6 +86,12 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
$rootScope.fablabWithoutOnlinePayment = Fablab.withoutOnlinePayment;
// Global config: if true, no invoices will be generated
$rootScope.fablabWithoutInvoices = Fablab.withoutInvoices;
// Global config: if true, the phone number is required to create an account
$rootScope.phoneRequired = Fablab.phoneRequired;
// Global config: if true, the events are shown in the admin calendar
$rootScope.eventsInCalendar = Fablab.eventsInCalendar;
// Global config: machine/space slot duration
$rootScope.slotDuration = Fablab.slotDuration;
// Global function to allow the user to navigate to the previous screen (ie. $state).
// If no previous $state were recorded, navigate to the home page

View File

@ -17,21 +17,21 @@ Application.Controllers.controller('AbusesController', ['$scope', '$state', 'Abu
resolve: {
object () {
return {
title: _t('manage_abuses.confirmation_required'),
msg: _t('manage_abuses.report_will_be_destroyed')
title: _t('app.admin.manage_abuses.confirmation_required'),
msg: _t('app.admin.manage_abuses.report_will_be_destroyed')
};
}
}
},
function () { // cancel confirmed
Abuse.remove({ id: abuseId }, function () { // successfully canceled
growl.success(_t('manage_abuses.report_removed'));
growl.success(_t('app.admin.manage_abuses.report_removed'));
Abuse.query({}, function (abuses) {
$scope.abuses = abuses.abuses.filter(a => a.signaled_type === 'Project');
});
}
, function () { // error while canceling
growl.error(_t('manage_abuses.failed_to_remove'));
growl.error(_t('app.admin.manage_abuses.failed_to_remove'));
});
}
);

View File

@ -54,14 +54,16 @@ const check_oauth2_id_is_mapped = function (mappings) {
* - $scope.authMethods
* - $scope.mappingFields
* - $scope.cancel()
* - $scope.methodName()
* - $scope.defineDataMapping(mapping)
*
* Requires :
* - mappingFieldsPromise: retrieved by AuthProvider.mapping_fields()
* - $state (Ui-Router) [ 'app.admin.members' ]
* - _t : translation method
*/
class AuthenticationController {
constructor ($scope, $state, $uibModal, mappingFieldsPromise) {
constructor ($scope, $state, $uibModal, _t, mappingFieldsPromise) {
// list of supported authentication methods
$scope.authMethods = METHODS;
@ -73,6 +75,13 @@ class AuthenticationController {
*/
$scope.cancel = function () { $state.go('app.admin.members'); };
/**
* Return a localized string for the provided method
*/
$scope.methodName = function(method) {
return _t('app.shared.authentication.' + METHODS[method]);
}
/**
* Open a modal allowing to specify the data mapping for the given field
*/
@ -137,9 +146,9 @@ class AuthenticationController {
$scope.ok = function () { $uibModalInstance.close($scope.transformation.rules); };
// do not save the modifications
return $scope.cancel = function () { $uibModalInstance.dismiss(); };
}
] })
$scope.cancel = function () { $uibModalInstance.dismiss(); };
}]
})
.result['finally'](null).then(function (transfo_rules) { mapping.transformation = transfo_rules; });
};
}
@ -163,9 +172,9 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
$scope.getType = function (type) {
const text = METHODS[type];
if (typeof text !== 'undefined') {
return _t(text);
return _t(`app.admin.members.authentication_form.${text}`);
} else {
return _t('unknown') + type;
return _t('app.admin.members.authentication_form.unknown') + type;
}
};
@ -176,10 +185,10 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
*/
$scope.getState = function (status) {
switch (status) {
case 'active': return _t('active');
case 'pending': return _t('pending');
case 'previous': return _t('previous_provider');
default: return _t('unknown') + status;
case 'active': return _t('app.admin.members.authentication_form.active');
case 'pending': return _t('app.admin.members.authentication_form.pending');
case 'previous': return _t('app.admin.members.authentication_form.previous_provider');
default: return _t('app.admin.members.authentication_form.unknown') + status;
}
};
@ -194,8 +203,8 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_the_TYPE_authentication_provider_NAME', { TYPE: $scope.getType(provider.providable_type), NAME: provider.name })
title: _t('app.admin.members.authentication_form.confirmation_required'),
msg: _t('app.admin.members.authentication_form.do_you_really_want_to_delete_the_TYPE_authentication_provider_NAME', { TYPE: $scope.getType(provider.providable_type), NAME: provider.name })
};
}
}
@ -206,9 +215,9 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
{ id: provider.id },
function () {
providers.splice(findIdxById(providers, provider.id), 1);
growl.success(_t('authentication_provider_successfully_deleted'));
growl.success(_t('app.admin.members.authentication_form.authentication_provider_successfully_deleted'));
},
function () { growl.error(_t('an_error_occurred_unable_to_delete_the_specified_provider')); }
function () { growl.error(_t('app.admin.members.authentication_form.an_error_occurred_unable_to_delete_the_specified_provider')); }
);
}
);
@ -254,19 +263,19 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
// prevent from adding mode than 1
for (provider of Array.from(authProvidersPromise)) {
if (provider.providable_type === 'DatabaseProvider') {
growl.error(_t('a_local_database_provider_already_exists_unable_to_create_another'));
growl.error(_t('app.admin.authentication_new.a_local_database_provider_already_exists_unable_to_create_another'));
return false;
}
}
return AuthProvider.save({ auth_provider: $scope.provider }, function (provider) {
growl.success(_t('local_provider_successfully_saved'));
growl.success(_t('app.admin.authentication_new.local_provider_successfully_saved'));
return $state.go('app.admin.members');
});
// === OAuth2Provider ===
} else if ($scope.provider.providable_type === 'OAuth2Provider') {
// check the ID mapping
if (!check_oauth2_id_is_mapped($scope.provider.providable_attributes.o_auth2_mappings_attributes)) {
growl.error(_t('it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider'));
growl.error(_t('app.admin.authentication_new.it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider'));
return false;
}
// discourage the use of unsecure SSO
@ -277,24 +286,24 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
resolve: {
object () {
return {
title: _t('security_issue_detected'),
msg: _t('beware_the_oauth2_authenticatoin_provider_you_are_about_to_add_isnt_using_HTTPS') +
_t('this_is_a_serious_security_issue_on_internet_and_should_never_be_used_except_for_testing_purposes') +
_t('do_you_really_want_to_continue')
title: _t('app.admin.authentication_new.security_issue_detected'),
msg: _t('app.admin.authentication_new.beware_the_oauth2_authenticatoin_provider_you_are_about_to_add_isnt_using_HTTPS') +
_t('app.admin.authentication_new.this_is_a_serious_security_issue_on_internet_and_should_never_be_used_except_for_testing_purposes') +
_t('app.admin.authentication_new.do_you_really_want_to_continue')
};
}
}
},
function () { // unsecured http confirmed
AuthProvider.save({ auth_provider: $scope.provider }, function (provider) {
growl.success(_t('unsecured_oauth2_provider_successfully_added'));
growl.success(_t('app.admin.authentication_new.unsecured_oauth2_provider_successfully_added'));
return $state.go('app.admin.members');
});
}
);
} else {
AuthProvider.save({ auth_provider: $scope.provider }, function (provider) {
growl.success(_t('oauth2_provider_successfully_added'));
growl.success(_t('app.admin.authentication_new.oauth2_provider_successfully_added'));
return $state.go('app.admin.members');
});
}
@ -302,7 +311,7 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
};
// Using the AuthenticationController
return new AuthenticationController($scope, $state, $uibModal, mappingFieldsPromise);
return new AuthenticationController($scope, $state, $uibModal, _t, mappingFieldsPromise);
}
]);
@ -322,21 +331,21 @@ Application.Controllers.controller('EditAuthenticationController', ['$scope', '$
$scope.updateProvider = function () {
// check the ID mapping
if (!check_oauth2_id_is_mapped($scope.provider.providable_attributes.o_auth2_mappings_attributes)) {
growl.error(_t('it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider'));
growl.error(_t('app.admin.authentication_edit.it_is_required_to_set_the_matching_between_User.uid_and_the_API_to_add_this_provider'));
return false;
}
return AuthProvider.update(
{ id: $scope.provider.id },
{ auth_provider: $scope.provider },
function (provider) {
growl.success(_t('provider_successfully_updated'));
growl.success(_t('app.admin.authentication_edit.provider_successfully_updated'));
$state.go('app.admin.members');
},
function () { growl.error(_t('an_error_occurred_unable_to_update_the_provider')); }
function () { growl.error(_t('app.admin.authentication_edit.an_error_occurred_unable_to_update_the_provider')); }
);
};
// Using the AuthenticationController
return new AuthenticationController($scope, $state, $uibModal, mappingFieldsPromise);
return new AuthenticationController($scope, $state, $uibModal, _t, mappingFieldsPromise);
}
]);

View File

@ -23,14 +23,13 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
/* PRIVATE STATIC CONSTANTS */
// The calendar is divided in slots of 30 minutes
let loadingCb;
const BASE_SLOT = '00:30:00';
// The bookings can be positioned every half hours
const BOOKING_SNAP = '00:30:00';
// We do not allow the creation of slots that are not a multiple of 60 minutes
const SLOT_MULTIPLE = 60;
const SLOT_MULTIPLE = Fablab.slotDuration;
/* PUBLIC SCOPE */
@ -40,6 +39,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
// currently selected availability
$scope.availability = null;
// corresponding fullCalendar item in the DOM
$scope.availabilityDom = null;
// bind the availabilities slots with full-Calendar events
$scope.eventSources = [];
$scope.eventSources.push({
@ -62,7 +64,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
return calendarEventClickCb(event, jsEvent, view);
},
eventRender (event, element, view) {
return eventRenderCb(event, element);
return eventRenderCb(event, element, view);
},
viewRender(view, element) {
return viewRenderCb(view, element);
},
loading (isLoading, view) {
return loadingCb(isLoading, view);
@ -80,10 +85,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
resolve: {
object () {
return {
title: _t('admin_calendar.confirmation_required'),
msg: _t('admin_calendar.do_you_really_want_to_cancel_the_USER_s_reservation_the_DATE_at_TIME_concerning_RESERVATION'
, { GENDER: getGender($scope.currentUser), USER: slot.user.name, DATE: moment(slot.start_at).format('L'), TIME: moment(slot.start_at).format('LT'), RESERVATION: slot.reservable.name }
, 'messageformat')
title: _t('app.admin.calendar.confirmation_required'),
msg: _t('app.admin.calendar.do_you_really_want_to_cancel_the_USER_s_reservation_the_DATE_at_TIME_concerning_RESERVATION'
, { GENDER: getGender($scope.currentUser), USER: slot.user.name, DATE: moment(slot.start_at).format('L'), TIME: moment(slot.start_at).format('LT'), RESERVATION: slot.reservable.name })
};
}
}
@ -101,10 +105,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}
}
// notify the admin
return growl.success(_t('admin_calendar.reservation_was_successfully_cancelled'));
return growl.success(_t('app.admin.calendar.reservation_was_successfully_cancelled'));
},
function (data, status) { // failed
growl.error(_t('admin_calendar.reservation_cancellation_failed'));
growl.error(_t('app.admin.calendar.reservation_cancellation_failed'));
}
);
}
@ -118,17 +122,17 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
*/
$scope.removeMachine = function (machine) {
if ($scope.availability.machine_ids.length === 1) {
return growl.error(_t('admin_calendar.unable_to_remove_the_last_machine_of_the_slot_delete_the_slot_rather'));
return growl.error(_t('app.admin.calendar.unable_to_remove_the_last_machine_of_the_slot_delete_the_slot_rather'));
} else {
// open a confirmation dialog
return dialogs.confirm({
resolve: {
object () {
return {
title: _t('admin_calendar.confirmation_required'),
msg: _t('admin_calendar.do_you_really_want_to_remove_MACHINE_from_this_slot', { GENDER: getGender($scope.currentUser), MACHINE: machine.name }, 'messageformat') + ' ' +
_t('admin_calendar.this_will_prevent_any_new_reservation_on_this_slot_but_wont_cancel_those_existing') + ' ' +
_t('admin_calendar.beware_this_cannot_be_reverted')
title: _t('app.admin.calendar.confirmation_required'),
msg: _t('app.admin.calendar.do_you_really_want_to_remove_MACHINE_from_this_slot', { GENDER: getGender($scope.currentUser), MACHINE: machine.name }) + ' ' +
_t('app.admin.calendar.this_will_prevent_any_new_reservation_on_this_slot_but_wont_cancel_those_existing') + '<br><strong>' +
_t('app.admin.calendar.beware_this_cannot_be_reverted') + '</strong>'
};
}
}
@ -150,10 +154,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
$scope.availability.title = data.title;
uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents');
// notify the admin
return growl.success(_t('admin_calendar.the_machine_was_successfully_removed_from_the_slot'));
return growl.success(_t('app.admin.calendar.the_machine_was_successfully_removed_from_the_slot'));
}
, function (data, status) { // failed
growl.error(_t('admin_calendar.deletion_failed'));
growl.error(_t('app.admin.calendar.deletion_failed'));
}
);
});
@ -167,7 +171,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
$scope.alertExport = function (type) {
Export.status({ category: 'availabilities', type }).then(function (res) {
if (!res.data.exists) {
return growl.success(_t('admin_calendar.export_is_running_you_ll_be_notified_when_its_ready'));
return growl.success(_t('app.admin.calendar.export_is_running_you_ll_be_notified_when_its_ready'));
}
});
};
@ -195,8 +199,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
resolve: {
object () {
return {
title: _t('admin_calendar.confirmation_required'),
msg: locked ? _t('admin_calendar.do_you_really_want_to_allow_reservations') : _t('admin_calendar.do_you_really_want_to_block_this_slot')
title: _t('app.admin.calendar.confirmation_required'),
msg: locked ? _t('app.admin.calendar.do_you_really_want_to_allow_reservations') : _t('app.admin.calendar.do_you_really_want_to_block_this_slot')
};
}
}
@ -208,18 +212,18 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
{ lock: !locked },
function (data) { // success
$scope.availability = data;
growl.success(locked ? _t('admin_calendar.unlocking_success') : _t('admin_calendar.locking_success'));
growl.success(locked ? _t('app.admin.calendar.unlocking_success') : _t('app.admin.calendar.locking_success'));
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
},
function (error) { // failed
growl.error(locked ? _t('admin_calendar.unlocking_failed') : _t('admin_calendar.locking_failed'));
growl.error(locked ? _t('app.admin.calendar.unlocking_failed') : _t('app.admin.calendar.locking_failed'));
console.error(error);
}
);
}
);
} else {
return growl.error(_t('admin_calendar.unlockable_because_reservations'));
return growl.error(_t('app.admin.calendar.unlockable_because_reservations'));
}
};
@ -228,32 +232,24 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
*/
$scope.removeSlot = function () {
// open a confirmation dialog
dialogs.confirm(
{
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "admin/calendar/deleteRecurrent.html" %>',
size: 'md',
controller: 'DeleteRecurrentAvailabilityController',
resolve: {
object () {
return {
title: _t('admin_calendar.confirmation_required'),
msg: _t('admin_calendar.do_you_really_want_to_delete_this_slot')
};
availabilityPromise: ['Availability', function (Availability) { return Availability.get({ id: $scope.availability.id }).$promise; }]
}
}
},
function () {
// the admin has confirmed, delete the slot
Availability.delete(
{ id: $scope.availability.id },
function () {
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents', $scope.availability.id);
growl.success(_t('admin_calendar.the_slot_START-END_has_been_successfully_deleted', { START: moment(event.start).format('LL LT'), END: moment(event.end).format('LT') }));
$scope.availability = null;
},
function () {
growl.error(_t('admin_calendar.unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member', { START: moment(event.start).format('LL LT'), END: moment(event.end).format('LT') }));
});
// once the dialog was closed, do things depending on the result
modalInstance.result.then(function (res) {
if (res.status == 'success') {
$scope.availability = null;
}
);
for (const availability of res.availabilities) {
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents', availability);
}
});
};
/* PRIVATE SCOPE */
@ -275,10 +271,22 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
var calendarSelectCb = function (start, end, jsEvent, view) {
start = moment.tz(start.toISOString(), Fablab.timezone);
end = moment.tz(end.toISOString(), Fablab.timezone);
// first we check that the selected slot is an N-hours multiple (ie. not decimal)
if (Number.isInteger(parseInt((end.valueOf() - start.valueOf()) / (SLOT_MULTIPLE * 1000), 10) / SLOT_MULTIPLE)) {
// check if slot is not in the past
const today = new Date();
if (parseInt((start.valueOf() - today) / (60 * 1000), 10) >= 0) {
if (Math.trunc((start.valueOf() - today) / (60 * 1000)) < 0) {
growl.warning(_t('app.admin.calendar.event_in_the_past'));
return uiCalendarConfig.calendars.calendar.fullCalendar('unselect');
}
// check that the selected slot is an multiple of SLOT_MULTIPLE (ie. not decimal)
const slots = Math.trunc((end.valueOf() - start.valueOf()) / (60 * 1000)) / SLOT_MULTIPLE;
if (!Number.isInteger(slots)) {
// otherwise, round it to upper decimal
const upper = Math.ceil(slots) * SLOT_MULTIPLE;
end = moment(start).add(upper, 'minutes');
}
// 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" %>',
@ -288,7 +296,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
end () { return end; },
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }]
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }],
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }]
} });
// when the modal is closed, we send the slot to the server for saving
modalInstance.result.then(
@ -312,8 +321,6 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
},
function () { uiCalendarConfig.calendars.calendar.fullCalendar('unselect'); }
);
}
}
return uiCalendarConfig.calendars.calendar.fullCalendar('unselect');
};
@ -325,6 +332,12 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
var calendarEventClickCb = function (event, jsEvent, view) {
$scope.availability = event;
if ($scope.availabilityDom) {
$scope.availabilityDom.classList.remove("fc-selected")
}
$scope.availabilityDom = jsEvent.target.closest('.fc-event');
$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')) {
return $scope.removeSlot();
@ -340,7 +353,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element) {
if (event.available_type !== 'event') {
element.find('.fc-content').prepend('<span class="remove-event">x&nbsp;</span>');
}
if (event.tags.length > 0) {
let html = '';
for (let tag of Array.from(event.tags)) {
@ -355,12 +370,23 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* Triggered when resource fetching starts/stops.
* @see https://fullcalendar.io/docs/resource_data/loading/
*/
return loadingCb = function (isLoading, view) {
const loadingCb = function (isLoading, view) {
if (isLoading) {
// we remove existing events when fetching starts to prevent duplicates
return uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
}
};
/**
* Triggered when the view is changed
* @see https://fullcalendar.io/docs/v3/viewRender#v2
*/
const viewRenderCb = function(view, element) {
// we unselect the current event to keep consistency
$scope.availability = null;
$scope.availabilityDom = null;
};
}
]);
@ -368,8 +394,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
/**
* Controller used in the slot creation modal window
*/
Application.Controllers.controller('CreateEventModalController', ['$scope', '$uibModalInstance', 'moment', 'start', 'end', 'machinesPromise', 'Availability', 'trainingsPromise', 'spacesPromise', 'Tag', 'growl', '_t',
function ($scope, $uibModalInstance, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, Tag, growl, _t) {
Application.Controllers.controller('CreateEventModalController', ['$scope', '$uibModalInstance', '$sce', 'moment', 'start', 'end', 'machinesPromise', 'Availability', 'trainingsPromise', 'spacesPromise', 'tagsPromise', 'growl', '_t',
function ($scope, $uibModalInstance, $sce, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, tagsPromise, growl, _t) {
// $uibModal parameter
$scope.start = start;
@ -385,6 +411,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
// spaces list
$scope.spaces = spacesPromise.filter(function (s) { return !s.disabled; });
// all tags list
$scope.tags = tagsPromise;
// machines associated with the created slot
$scope.selectedMachines = [];
@ -416,9 +445,23 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.availability = {
start_at: start,
end_at: end,
available_type: 'machines' // default
available_type: 'machines', // default
tag_ids: [],
is_recurrent: false,
period: 'week',
nb_periods: 1,
end_date: undefined // recurrence end
};
// recurrent slots
$scope.occurrences = [];
// localized name(s) of the reservable item(s)
$scope.reservableName = '';
// localized name(s) of the selected tag(s)
$scope.tagsName = '';
/**
* Adds or removes the provided machine from the current slot
* @param machine {Object}
@ -440,7 +483,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
if ($scope.selectedMachines.length > 0) {
$scope.availability.machine_ids = $scope.selectedMachines.map(function (m) { return m.id; });
} else {
growl.error(_t('admin_calendar.you_should_select_at_least_a_machine'));
growl.error(_t('app.admin.calendar.you_should_select_at_least_a_machine'));
return;
}
} else if ($scope.availability.available_type === 'training') {
@ -448,9 +491,13 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
} else if ($scope.availability.available_type === 'space') {
$scope.availability.space_ids = [$scope.selectedSpace.id];
}
if ($scope.availability.is_recurrent) {
$scope.availability.occurrences = $scope.occurrences;
}
return Availability.save(
{ availability: $scope.availability }
, function (availability) { $uibModalInstance.close(availability); });
{ availability: $scope.availability },
function (availability) { $uibModalInstance.close(availability); }
);
};
/**
@ -458,6 +505,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
*/
$scope.next = function () {
if ($scope.step === 1) { $scope.setNbTotalPlaces(); }
if ($scope.step === 2) { return validateSelection(); }
if ($scope.step === 4) { return validateRecurrence(); }
return $scope.step++;
};
@ -495,23 +544,25 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.selectedSpace = $scope.spaces[0];
}
Tag.query().$promise.then(function (data) { $scope.tags = data; });
// When we configure a machine availability, do not let the user change the end time, as the total
// time must be dividable by 60 minutes (base slot duration). For training availabilities, the user
// When we configure a machine/space availability, do not let the user change the end time, as the total
// time must be dividable by Fablab.slotDuration minutes (base slot duration). For training availabilities, the user
// can configure any duration as it does not matters.
$scope.$watch('availability.available_type', function (newValue, oldValue, scope) {
if ((newValue === 'machines') || (newValue === 'space')) {
$scope.endDateReadOnly = true;
const diff = moment($scope.end).diff($scope.start, 'hours'); // the result is rounded down by moment.js
$scope.end = moment($scope.start).add(diff, 'hours').toDate();
const slots = Math.trunc(($scope.end.valueOf() - $scope.start.valueOf()) / (60 * 1000)) / Fablab.slotDuration;
if (!Number.isInteger(slots)) {
// otherwise, round it to upper decimal
const upper = Math.ceil(slots) * Fablab.slotDuration;
$scope.end = moment($scope.start).add(upper, 'minutes').toDate();
}
return $scope.availability.end_at = $scope.end;
} else {
return $scope.endDateReadOnly = false;
}
});
// When the start date is changed, if we are configuring a machine availability,
// When the start date is changed, if we are configuring a machine/space availability,
// maintain the relative length of the slot (ie. change the end time accordingly)
$scope.$watch('start', function (newValue, oldValue, scope) {
// for machine or space availabilities, adjust the end time
@ -520,8 +571,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
end.add(moment(newValue).diff(oldValue), 'milliseconds');
$scope.end = end.toDate();
} else { // for training availabilities
// prevent the admin from setting the begining after the and
if (moment(newValue).add(1, 'hour').isAfter($scope.end)) {
// prevent the admin from setting the beginning after the end
if (moment(newValue).add(Fablab.slotDuration, 'minutes').isAfter($scope.end)) {
$scope.start = oldValue;
}
}
@ -531,8 +582,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
// Maintain consistency between the end time and the date object in the availability object
return $scope.$watch('end', function (newValue, oldValue, scope) {
// we prevent the admin from setting the end of the availability before its begining
if (moment($scope.start).add(1, 'hour').isAfter(newValue)) {
// we prevent the admin from setting the end of the availability before its beginning
if (moment($scope.start).add(Fablab.slotDuration, 'minutes').isAfter(newValue)) {
$scope.end = oldValue;
}
// update availability object
@ -540,7 +591,270 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
});
};
/**
* Validates that a machine or more was/were selected before continuing to step 3 (adjust time + tags)
*/
const validateSelection = function () {
if ($scope.availability.available_type === 'machines') {
if ($scope.selectedMachines.length === 0) {
return growl.error(_t('app.admin.calendar.you_should_select_at_least_a_machine'));
}
}
$scope.step++;
};
/**
* Validates that the recurrence parameters were correctly set before continuing to step 5 (summary)
*/
const validateRecurrence = function () {
if ($scope.availability.is_recurrent) {
if (!$scope.availability.period) {
return growl.error(_t('app.admin.calendar.select_period'));
}
if (!$scope.availability.nb_periods) {
return growl.error(_t('app.admin.calendar.select_nb_period'));
}
if (!$scope.availability.end_date) {
return growl.error(_t('app.admin.calendar.select_end_date'));
}
}
// settings are ok
computeOccurrences();
computeNames();
$scope.step++;
};
/**
* Compute the various occurrences of the availability, according to the recurrence settings
*/
const computeOccurrences = function () {
$scope.occurrences = [];
if ($scope.availability.is_recurrent) {
const date = moment($scope.availability.start_at);
const diff = moment($scope.availability.end_at).diff($scope.availability.start_at);
const end = moment($scope.availability.end_date).endOf('day');
while (date.isBefore(end)) {
const occur_end = moment(date).add(diff, 'ms');
$scope.occurrences.push({
start_at: date.toDate(),
end_at: occur_end.toDate()
});
date.add($scope.availability.nb_periods, $scope.availability.period);
}
} else {
$scope.occurrences.push({
start_at: $scope.availability.start_at,
end_at: $scope.availability.end_at
});
}
};
const computeNames = function () {
$scope.reservableName = '';
switch ($scope.availability.available_type) {
case 'machines':
$scope.reservableName = localizedList($scope.selectedMachines)
break;
case 'training':
$scope.reservableName = `<strong>${$scope.selectedTraining.name}</strong>`;
break;
case 'space':
$scope.reservableName = `<strong>${$scope.selectedSpace.name}</strong>`;
break;
default:
$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);
}
const localizedList = function (items) {
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();
}
]);
/**
* Controller used in the slot deletion modal window
*/
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;
// with recurrent slots: how many slots should we delete?
$scope.deleteMode = 'single';
/**
* Confirmation callback
*/
$scope.ok = function () {
const { id, start_at, end_at } = availabilityPromise;
// the admin has confirmed, delete the slot
Availability.delete(
{ id, mode: $scope.deleteMode },
function (res) {
// delete success
if (res.deleted > 1) {
growl.success(_t(
'app.admin.calendar.slots_deleted',
{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')}
));
}
$uibModalInstance.close({
status: 'success',
availabilities: res.details.map(function (d) { return d.availability.id })
});
},
function (res) {
// not everything was deleted
const { data } = res;
if (data.total > 1) {
growl.warning(_t(
'app.admin.calendar.slots_not_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')}
));
}
$uibModalInstance.close({
status: 'failed',
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
*/
Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCalendars', 'ICalendar', 'dialogs', 'growl', '_t',
function ($scope, iCalendars, ICalendar, dialogs, growl, _t) {
// list of ICS sources
$scope.calendars = iCalendars;
// configuration of a new ICS source
$scope.newCalendar = {
color: undefined,
text_color: undefined,
url: undefined,
name: undefined,
text_hidden: false
};
/**
* Save the new iCalendar in database
*/
$scope.save = function () {
ICalendar.save({}, { i_calendar: $scope.newCalendar }, function (data) {
// success
$scope.calendars.push(data);
$scope.newCalendar.url = undefined;
$scope.newCalendar.name = undefined;
$scope.newCalendar.color = null;
$scope.newCalendar.text_color = null;
$scope.newCalendar.text_hidden = false;
}, function (error) {
// failed
growl.error(_t('app.admin.icalendar.create_error'));
console.error(error);
})
}
/**
* Return a CSS-like style of the given calendar configuration
* @param calendar
*/
$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'
};
}
/**
* Delete the given calendar from the database
* @param calendar
*/
$scope.delete = function (calendar) {
dialogs.confirm(
{
resolve: {
object () {
return {
title: _t('app.admin.icalendar.confirmation_required'),
msg: _t('app.admin.icalendar.confirm_delete_import')
};
}
}
},
function () {
ICalendar.delete(
{ id: calendar.id },
function () {
// success
const idx = $scope.calendars.indexOf(calendar);
$scope.calendars.splice(idx, 1);
growl.info(_t('app.admin.icalendar.delete_success'));
}, function (error) {
// failed
growl.error(_t('app.admin.icalendar.delete_failed'));
console.error(error);
}
);
}
)
}
/**
* Asynchronously re-fetches the events from the given calendar
* @param calendar
*/
$scope.sync = function (calendar) {
ICalendar.sync(
{ id: calendar.id },
function () {
// success
growl.info(_t('app.admin.icalendar.refresh'));
}, function (error) {
// failed
growl.error(_t('app.admin.icalendar.sync_failed'));
console.error(error);
}
)
}
}
]);

View File

@ -1,19 +1,8 @@
/* eslint-disable
no-return-assign,
no-undef,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
/* COMMON CODE */
// The validity per user defines how many time a user may ba able to use the same coupon
// Here are the various options for this parameter
const userValidities = ['once', 'forever'];
const VALIDITIES = ['once', 'forever'];
/**
* Controller used in the coupon creation page
@ -27,7 +16,7 @@ Application.Controllers.controller('NewCouponController', ['$scope', '$state', '
};
// Options for the validity per user
$scope.validities = userValidities;
$scope.validities = VALIDITIES;
// Default parameters for AngularUI-Bootstrap datepicker (used for coupon validity limit selection)
$scope.datePicker = {
@ -39,6 +28,13 @@ Application.Controllers.controller('NewCouponController', ['$scope', '$state', '
}
};
/**
* Return a localized human-readable name for the provided validity
*/
$scope.validityName = function (validity) {
return _t(`app.shared.coupon.${validity}`);
};
/**
* Shows/hides the validity limit datepicker
* @param $event {Object} jQuery event object
@ -46,17 +42,17 @@ Application.Controllers.controller('NewCouponController', ['$scope', '$state', '
$scope.toggleDatePicker = function ($event) {
$event.preventDefault();
$event.stopPropagation();
return $scope.datePicker.opened = !$scope.datePicker.opened;
$scope.datePicker.opened = !$scope.datePicker.opened;
};
/**
* Callback to save the new coupon in $scope.coupon and redirect the user to the listing page
*/
return $scope.saveCoupon = () =>
$scope.saveCoupon = () =>
Coupon.save({ coupon: $scope.coupon }, coupon => $state.go('app.admin.pricing')
, function (err) {
growl.error(_t('unable_to_create_the_coupon_check_code_already_used'));
return console.error(err);
growl.error(_t('app.admin.coupons_new.unable_to_create_the_coupon_check_code_already_used'));
console.error(err);
});
}
]);
@ -75,7 +71,7 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
$scope.coupon = couponPromise;
// Options for the validity per user
$scope.validities = userValidities;
$scope.validities = VALIDITIES;
// Mapping for validation errors
$scope.errors = {};
@ -90,6 +86,13 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
}
};
/**
* Return a localized human-readable name for the provided validity
*/
$scope.validityName = function (validity) {
return _t(`app.shared.coupon.${validity}`);
};
/**
* Shows/hides the validity limit datepicker
* @param $event {Object} jQuery event object
@ -97,7 +100,7 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
$scope.toggleDatePicker = function ($event) {
$event.preventDefault();
$event.stopPropagation();
return $scope.datePicker.opened = !$scope.datePicker.opened;
$scope.datePicker.opened = !$scope.datePicker.opened;
};
/**
@ -105,10 +108,10 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
*/
$scope.updateCoupon = function () {
$scope.errors = {};
return Coupon.update({ id: $scope.coupon.id }, { coupon: $scope.coupon }, coupon => $state.go('app.admin.pricing')
Coupon.update({ id: $scope.coupon.id }, { coupon: $scope.coupon }, coupon => $state.go('app.admin.pricing')
, function (err) {
growl.error(_t('unable_to_update_the_coupon_an_error_occurred'));
return $scope.errors = err.data;
growl.error(_t('app.admin.coupons_edit.unable_to_update_the_coupon_an_error_occurred'));
$scope.errors = err.data;
});
};
@ -120,7 +123,7 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
const initialize = function () {
// parse the date if any
if (couponPromise.valid_until) {
return $scope.coupon.valid_until = moment(couponPromise.valid_until).toDate();
$scope.coupon.valid_until = moment(couponPromise.valid_until).toDate();
}
};

View File

@ -228,26 +228,26 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
*/
$scope.removeElement = function (model, index) {
if ((model === 'category') && (getModel(model)[1].length === 1)) {
growl.error(_t('at_least_one_category_is_required') + ' ' + _t('unable_to_delete_the_last_one'));
growl.error(_t('app.admin.events.at_least_one_category_is_required') + ' ' + _t('app.admin.events.unable_to_delete_the_last_one'));
return false;
}
if (getModel(model)[1][index].related_to > 0) {
growl.error(_t('unable_to_delete_ELEMENT_already_in_use_NUMBER_times', { ELEMENT: model, NUMBER: getModel(model)[1][index].related_to }, 'messageformat'));
growl.error(_t('app.admin.events.unable_to_delete_ELEMENT_already_in_use_NUMBER_times', { ELEMENT: model, NUMBER: getModel(model)[1][index].related_to }));
return false;
}
return dialogs.confirm({
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_ELEMENT', { ELEMENT: model }, 'messageformat')
title: _t('app.admin.events.confirmation_required'),
msg: _t('app.admin.events.do_you_really_want_to_delete_this_ELEMENT', { ELEMENT: model })
};
}
}
}
, function () { // delete confirmed
getModel(model)[0].delete(getModel(model)[1][index], null, function () { getModel(model)[1].splice(index, 1); }
, function () { growl.error(_t('unable_to_delete_an_error_occured')); });
, function () { growl.error(_t('app.admin.events.unable_to_delete_an_error_occured')); });
});
};
@ -292,10 +292,10 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
// save the price category to the API
PriceCategory.save(p_cat, function (cat) {
$scope.priceCategories.push(cat);
return growl.success(_t('price_category_successfully_created'));
return growl.success(_t('app.admin.events.price_category_successfully_created'));
}
, function (err) {
growl.error(_t('unable_to_add_the_price_category_check_name_already_used'));
growl.error(_t('app.admin.events.unable_to_add_the_price_category_check_name_already_used'));
return console.error(err);
});
});
@ -308,7 +308,7 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
*/
$scope.editPriceCategory = function (id, index) {
if ($scope.priceCategories[index].id !== id) {
return growl.error(_t('unexpected_error_occurred_please_refresh'));
return growl.error(_t('app.admin.events.unexpected_error_occurred_please_refresh'));
} else {
return $uibModal.open({
templateUrl: '<%= asset_path "admin/events/price_form.html" %>',
@ -320,10 +320,10 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
// update the price category to the API
PriceCategory.update({ id }, { price_category: p_cat }, function (cat) {
$scope.priceCategories[index] = cat;
return growl.success(_t('price_category_successfully_updated'));
return growl.success(_t('app.admin.events.price_category_successfully_updated'));
}
, function (err) {
growl.error(_t('unable_to_update_the_price_category'));
growl.error(_t('app.admin.events.unable_to_update_the_price_category'));
return console.error(err);
});
});
@ -337,17 +337,17 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
*/
$scope.removePriceCategory = function (id, index) {
if ($scope.priceCategories[index].id !== id) {
return growl.error(_t('unexpected_error_occurred_please_refresh'));
return growl.error(_t('app.admin.events.unexpected_error_occurred_please_refresh'));
} else if ($scope.priceCategories[index].events > 0) {
return growl.error(_t('unable_to_delete_this_price_category_because_it_is_already_used'));
return growl.error(_t('app.admin.events.unable_to_delete_this_price_category_because_it_is_already_used'));
} else {
return dialogs.confirm(
{
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_price_category')
title: _t('app.admin.events.confirmation_required'),
msg: _t('app.admin.events.do_you_really_want_to_delete_this_price_category')
};
}
}
@ -356,10 +356,10 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
PriceCategory.remove(
{ id },
function () { // successfully deleted
growl.success(_t('price_category_successfully_deleted'));
growl.success(_t('app.admin.events.price_category_successfully_deleted'));
$scope.priceCategories.splice(index, 1);
},
function () { growl.error(_t('price_category_deletion_failed')); }
function () { growl.error(_t('app.admin.events.price_category_deletion_failed')); }
);
}
);
@ -431,7 +431,16 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
$scope.event = eventPromise;
// list of reservations for the current event
return $scope.reservations = reservationsPromise;
$scope.reservations = reservationsPromise;
/**
* Test if the provided reservation has been cancelled
* @param reservation {Reservation}
* @returns {boolean}
*/
$scope.isCancelled = function(reservation) {
return !!(reservation.slots[0].canceled_at);
}
}]);
/**
@ -474,11 +483,11 @@ Application.Controllers.controller('NewEventController', ['$scope', '$state', 'C
// Possible types of recurrences for an event
$scope.recurrenceTypes = [
{ label: _t('none'), value: 'none' },
{ label: _t('every_days'), value: 'day' },
{ label: _t('every_week'), value: 'week' },
{ label: _t('every_month'), value: 'month' },
{ label: _t('every_year'), value: 'year' }
{ label: _t('app.admin.events_new.none'), value: 'none' },
{ label: _t('app.admin.events_new.every_days'), value: 'day' },
{ label: _t('app.admin.events_new.every_week'), value: 'week' },
{ label: _t('app.admin.events_new.every_month'), value: 'month' },
{ label: _t('app.admin.events_new.every_year'), value: 'year' }
];
// Using the EventsController

View File

@ -25,10 +25,10 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro
const CHART_HEIGHT = 500;
// Label of the charts' horizontal axes
const X_AXIS_LABEL = _t('date');
const X_AXIS_LABEL = _t('app.admin.stats_graphs.date');
// Label of the charts' vertical axes
const Y_AXIS_LABEL = _t('number');
const Y_AXIS_LABEL = _t('app.admin.stats_graphs.number');
// Colors for the line charts. Each new line uses the next color in this array
const CHART_COLORS = ['#b35a94', '#1c5794', '#00b49e', '#6fac48', '#ebcf4a', '#fd7e33', '#ca3436', '#a26e3a'];
@ -193,9 +193,9 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro
}
} else if ($scope.display.interval === 'week') {
if ((typeof x === 'number') || d instanceof Date) {
return d3.time.format(_t('week_short') + ' %U')(moment(d).toDate());
return d3.time.format(_t('app.admin.stats_graphs.week_short') + ' %U')(moment(d).toDate());
} else if (typeof d === 'number') {
return _t('week_of_START_to_END', { START: moment(d).format('L'), END: moment(d).add(6, 'days').format('L') });
return _t('app.admin.stats_graphs.week_of_START_to_END', { START: moment(d).format('L'), END: moment(d).add(6, 'days').format('L') });
} else { // typeof d == 'string'
return d;
}
@ -653,7 +653,7 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro
// common for each charts
chart.margin({ left: 100, right: 100 });
chart.noData(_t('no_data_for_this_period'));
chart.noData(_t('app.admin.stats_graphs.no_data_for_this_period'));
chart.height(CHART_HEIGHT);
// add new chart to the page

View File

@ -54,15 +54,15 @@ Application.Controllers.controller('GroupsController', ['$scope', 'groupsPromise
*/
$scope.saveGroup = function (data, id) {
if (id != null) {
return Group.update({ id }, { group: data }, response => growl.success(_t('group_form.changes_successfully_saved'))
, error => growl.error(_t('group_form.an_error_occurred_while_saving_changes')));
return Group.update({ id }, { group: data }, response => growl.success(_t('app.admin.members.group_form.changes_successfully_saved'))
, error => growl.error(_t('app.admin.members.group_form.an_error_occurred_while_saving_changes')));
} else {
return Group.save({ group: data }, function (resp) {
growl.success(_t('group_form.new_group_successfully_saved'));
growl.success(_t('app.admin.members.group_form.new_group_successfully_saved'));
return $scope.groups[$scope.groups.length - 1].id = resp.id;
}
, function (error) {
growl.error(_t('.group_forman_error_occurred_when_saving_the_new_group'));
growl.error(_t('app.admin.members.group_form.an_error_occurred_when_saving_the_new_group'));
return $scope.groups.splice($scope.groups.length - 1, 1);
});
}
@ -74,10 +74,10 @@ Application.Controllers.controller('GroupsController', ['$scope', 'groupsPromise
*/
$scope.removeGroup = index =>
Group.delete({ id: $scope.groups[index].id }, function (resp) {
growl.success(_t('group_form.group_successfully_deleted'));
growl.success(_t('app.admin.members.group_form.group_successfully_deleted'));
return $scope.groups.splice(index, 1);
}
, error => growl.error(_t('group_form.unable_to_delete_group_because_some_users_and_or_groups_are_still_linked_to_it')));
, error => growl.error(_t('app.admin.members.group_form.unable_to_delete_group_because_some_users_and_or_groups_are_still_linked_to_it')));
/**
* Enable/disable the group at the specified index
@ -86,13 +86,13 @@ Application.Controllers.controller('GroupsController', ['$scope', 'groupsPromise
return $scope.toggleDisableGroup = function (index) {
const group = $scope.groups[index];
if (!group.disabled && (group.users > 0)) {
return growl.error(_t('group_form.unable_to_disable_group_with_users', { USERS: group.users }, 'messageformat'));
return growl.error(_t('app.admin.members.group_form.unable_to_disable_group_with_users', { USERS: group.users }));
} else {
return Group.update({ id: group.id }, { group: { disabled: !group.disabled } }, function (response) {
$scope.groups[index] = response;
return growl.success(_t('group_form.group_successfully_enabled_disabled', { STATUS: response.disabled }, 'messageformat'));
return growl.success(_t('app.admin.members.group_form.group_successfully_enabled_disabled', { STATUS: response.disabled }));
}
, error => growl.error(_t('group_form.unable_to_enable_disable_group', { STATUS: !group.disabled }, 'messageformat')));
, error => growl.error(_t('app.admin.members.group_form.unable_to_enable_disable_group', { STATUS: !group.disabled })));
}
};
}

View File

@ -215,7 +215,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
$scope.invoices.unshift(res.avoir);
return Invoice.get({ id: invoice.id }, function (data) {
invoice.has_avoir = data.has_avoir;
return growl.success(_t('invoices.refund_invoice_successfully_created'));
return growl.success(_t('app.admin.invoices.refund_invoice_successfully_created'));
});
});
};
@ -289,10 +289,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
modalInstance.result.then(function (model) {
Setting.update({ name: 'invoice_reference' }, { value: model }, function (data) {
$scope.invoice.reference.model = model;
growl.success(_t('invoices.invoice_reference_successfully_saved'));
growl.success(_t('app.admin.invoices.invoice_reference_successfully_saved'));
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_invoice_reference'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_invoice_reference'));
console.error(error);
});
});
@ -327,24 +327,24 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
Setting.update({ name: 'invoice_code-value' }, { value: result.model }, function (data) {
$scope.invoice.code.model = result.model;
if (result.active) {
return growl.success(_t('invoices.invoicing_code_succesfully_saved'));
return growl.success(_t('app.admin.invoices.invoicing_code_succesfully_saved'));
}
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_the_invoicing_code'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_invoicing_code'));
return console.error(error);
});
return Setting.update({ name: 'invoice_code-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
$scope.invoice.code.active = result.active;
if (result.active) {
return growl.success(_t('invoices.code_successfully_activated'));
return growl.success(_t('app.admin.invoices.code_successfully_activated'));
} else {
return growl.success(_t('invoices.code_successfully_disabled'));
return growl.success(_t('app.admin.invoices.code_successfully_disabled'));
}
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_activating_the_invoicing_code'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_invoicing_code'));
return console.error(error);
});
});
@ -373,10 +373,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return modalInstance.result.then(function (model) {
Setting.update({ name: 'invoice_order-nb' }, { value: model }, function (data) {
$scope.invoice.number.model = model;
return growl.success(_t('invoices.order_number_successfully_saved'));
return growl.success(_t('app.admin.invoices.order_number_successfully_saved'));
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_the_order_number'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_order_number'));
return console.error(error);
});
});
@ -431,24 +431,24 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
Setting.update({ name: 'invoice_VAT-rate' }, { value: result.rate + '' }, function (data) {
$scope.invoice.VAT.rate = result.rate;
if (result.active) {
return growl.success(_t('invoices.VAT_rate_successfully_saved'));
return growl.success(_t('app.admin.invoices.VAT_rate_successfully_saved'));
}
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_the_VAT_rate'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_VAT_rate'));
return console.error(error);
});
return Setting.update({ name: 'invoice_VAT-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
$scope.invoice.VAT.active = result.active;
if (result.active) {
return growl.success(_t('invoices.VAT_successfully_activated'));
return growl.success(_t('app.admin.invoices.VAT_successfully_activated'));
} else {
return growl.success(_t('invoices.VAT_successfully_disabled'));
return growl.success(_t('app.admin.invoices.VAT_successfully_disabled'));
}
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_activating_the_VAT'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_VAT'));
return console.error(error);
});
});
@ -461,10 +461,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
const parsed = parseHtml($scope.invoice.text.content);
return Setting.update({ name: 'invoice_text' }, { value: parsed }, function (data) {
$scope.invoice.text.content = parsed;
return growl.success(_t('invoices.text_successfully_saved'));
return growl.success(_t('app.admin.invoices.text_successfully_saved'));
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_the_text'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_text'));
return console.error(error);
});
};
@ -476,10 +476,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
const parsed = parseHtml($scope.invoice.legals.content);
return Setting.update({ name: 'invoice_legals' }, { value: parsed }, function (data) {
$scope.invoice.legals.content = parsed;
return growl.success(_t('invoices.address_and_legal_information_successfully_saved'));
return growl.success(_t('app.admin.invoices.address_and_legal_information_successfully_saved'));
}
, function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_the_address_and_the_legal_information'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_address_and_the_legal_information'));
return console.error(error);
});
};
@ -552,7 +552,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
$scope.save = function() {
Setting.bulkUpdate(
{ settings: Object.values($scope.settings) },
function () { growl.success(_t('invoices.codes_customization_success')); },
function () { growl.success(_t('app.admin.invoices.codes_customization_success')); },
function (error) {
growl.error('unexpected_error_occurred');
console.error(error);
@ -591,9 +591,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return Setting.update(
{ name: 'invoice_logo' },
{ value: $scope.invoice.logo.base64 },
function (data) { growl.success(_t('invoices.logo_successfully_saved')); },
function (data) { growl.success(_t('app.admin.invoices.logo_successfully_saved')); },
function (error) {
growl.error(_t('invoices.an_error_occurred_while_saving_the_logo'));
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_logo'));
return console.error(error);
}
);
@ -696,17 +696,17 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
// Possible refunding methods
$scope.avoirModes = [
{ name: _t('invoices.none'), value: 'none' },
{ name: _t('invoices.by_cash'), value: 'cash' },
{ name: _t('invoices.by_cheque'), value: 'cheque' },
{ name: _t('invoices.by_transfer'), value: 'transfer' },
{ name: _t('invoices.by_wallet'), value: 'wallet' }
{ name: _t('app.admin.invoices.none'), value: 'none' },
{ name: _t('app.admin.invoices.by_cash'), value: 'cash' },
{ name: _t('app.admin.invoices.by_cheque'), value: 'cheque' },
{ name: _t('app.admin.invoices.by_transfer'), value: 'transfer' },
{ name: _t('app.admin.invoices.by_wallet'), value: 'wallet' }
];
// If a subscription was took with the current invoice, should it be canceled or not
$scope.subscriptionExpireOptions = {};
$scope.subscriptionExpireOptions[_t('yes')] = true;
$scope.subscriptionExpireOptions[_t('no')] = false;
$scope.subscriptionExpireOptions[_t('app.shared.buttons.yes')] = true;
$scope.subscriptionExpireOptions[_t('app.shared.buttons.no')] = false;
// AngularUI-Bootstrap datepicker parameters to define when to refund
$scope.datePicker = {
@ -742,7 +742,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
}
if ($scope.avoir.invoice_items_ids.length === 0) {
return growl.error(_t('invoices.you_must_select_at_least_one_element_to_create_a_refund'));
return growl.error(_t('app.admin.invoices.you_must_select_at_least_one_element_to_create_a_refund'));
} else {
return Invoice.save(
{ avoir: $scope.avoir },
@ -750,7 +750,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
$uibModalInstance.close({ avoir, invoice: $scope.invoice });
},
function (err) { // failed
growl.error(_t('invoices.unable_to_create_the_refund'));
growl.error(_t('app.admin.invoices.unable_to_create_the_refund'));
}
);
}
@ -791,7 +791,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
});
if (invoice.stripe) {
return $scope.avoirModes.push({ name: _t('invoices.online_payment'), value: 'stripe' });
return $scope.avoirModes.push({ name: _t('app.admin.invoices.online_payment'), value: 'stripe' });
}
};
@ -861,16 +861,16 @@ Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$ui
resolve: {
object () {
return {
title: _t('invoices.confirmation_required'),
title: _t('app.admin.invoices.confirmation_required'),
msg: $sce.trustAsHtml(
_t(
'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('invoices.period_must_match_fiscal_year')
+ _t('app.admin.invoices.period_must_match_fiscal_year')
+ '</strong><br/><br/>'
+ _t('invoices.this_may_take_a_while')
+ _t('app.admin.invoices.this_may_take_a_while')
)
};
}
@ -895,7 +895,7 @@ Application.Controllers.controller('ClosePeriodModalController', ['$scope', '$ui
},
function(error) {
$scope.pendingCreation = false;
growl.error(_t('invoices.failed_to_close_period'));
growl.error(_t('app.admin.invoices.failed_to_close_period'));
$scope.errors = error.data;
}
);
@ -982,7 +982,7 @@ Application.Controllers.controller('AccountingExportModalController', ['$scope',
Export.status(statusQry).then(function (res) {
if (!res.data.exists) {
growl.success(_t('invoices.export_is_running'));
growl.success(_t('app.admin.invoices.export_is_running'));
}
$uibModalInstance.close(res);
});

View File

@ -182,6 +182,35 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
}
};
/**
* Ask for confirmation then delete the specified user
* @param memberId {number} identifier of the user to delete
*/
$scope.deleteMember = function(memberId) {
dialogs.confirm(
{
resolve: {
object () {
return {
title: _t('app.admin.members.confirmation_required'),
msg: $sce.trustAsHtml(_t('app.admin.members.confirm_delete_member') + '<br/><br/>' + _t('app.admin.members.this_may_take_a_while_please_wait'))
};
}
}
},
function () { // cancel confirmed
Member.delete(
{ id: memberId },
function () {
$scope.members.splice(findItemIdxById($scope.members, memberId), 1);
return growl.success(_t('app.admin.members.member_successfully_deleted'));
},
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_member')); }
);
}
);
}
/**
* Ask for confirmation then delete the specified administrator
* @param admins {Array} full list of administrators
@ -193,8 +222,8 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: $sce.trustAsHtml(_t('do_you_really_want_to_delete_this_administrator_this_cannot_be_undone') + '<br/><br/>' + _t('this_may_take_a_while_please_wait'))
title: _t('app.admin.members.confirmation_required'),
msg: $sce.trustAsHtml(_t('app.admin.members.do_you_really_want_to_delete_this_administrator_this_cannot_be_undone') + '<br/><br/>' + _t('app.admin.members.this_may_take_a_while_please_wait'))
};
}
}
@ -203,10 +232,10 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
Admin.delete(
{ id: admin.id },
function () {
admins.splice(findAdminIdxById(admins, admin.id), 1);
return growl.success(_t('administrator_successfully_deleted'));
admins.splice(findItemIdxById(admins, admin.id), 1);
return growl.success(_t('app.admin.members.administrator_successfully_deleted'));
},
function (error) { growl.error(_t('unable_to_delete_the_administrator')); }
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_administrator')); }
);
}
);
@ -239,7 +268,7 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
$scope.alertExport = function (type) {
Export.status({ category: 'users', type }).then(function (res) {
if (!res.data.exists) {
return growl.success(_t('export_is_running_you_ll_be_notified_when_its_ready'));
return growl.success(_t('app.admin.members.export_is_running_you_ll_be_notified_when_its_ready'));
}
});
};
@ -261,13 +290,13 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
var searchTimeout = null;
/**
* Iterate through the provided array and return the index of the requested admin
* @param admins {Array} full list of users with role 'admin'
* @param id {Number} user id of the admin to retrieve in the list
* @returns {Number} index of the requested admin, in the provided array
* Iterate through the provided array and return the index of the requested item
* @param items {Array} full list of users with role 'admin'
* @param id {Number} id of the item to retrieve in the list
* @returns {Number} index of the requested item, in the provided array
*/
var findAdminIdxById = function (admins, id) {
return (admins.map(function (admin) { return admin.id; })).indexOf(id);
var findItemIdxById = function (items, id) {
return (items.map(function (item) { return item.id; })).indexOf(id);
};
/**
@ -395,10 +424,10 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
{ id: subscription.id },
{ subscription: { expired_at: $scope.new_expired_at, free } },
function (_subscription) {
growl.success(_t('you_successfully_changed_the_expiration_date_of_the_user_s_subscription'));
growl.success(_t('app.admin.members_edit.you_successfully_changed_the_expiration_date_of_the_user_s_subscription'));
return $uibModalInstance.close(_subscription);
},
function (error) { growl.error(_t('a_problem_occurred_while_saving_the_date')); }
function (error) { growl.error(_t('app.admin.members_edit.a_problem_occurred_while_saving_the_date')); }
);
};
@ -442,12 +471,12 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.ok = function () {
$scope.subscription.user_id = user.id;
return Subscription.save({ }, { subscription: $scope.subscription }, function (_subscription) {
growl.success(_t('subscription_successfully_purchased'));
growl.success(_t('app.admin.members_edit.subscription_successfully_purchased'));
$uibModalInstance.close(_subscription);
return $state.reload();
}
, function (error) {
growl.error(_t('a_problem_occurred_while_taking_the_subscription'));
growl.error(_t('app.admin.members_edit.a_problem_occurred_while_taking_the_subscription'));
console.error(error);
});
};
@ -458,7 +487,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
}]
});
// once the form was validated succesfully ...
// once the form was validated successfully ...
return modalInstance.result.then(function (subscription) { $scope.subscription = subscription; });
};
@ -507,11 +536,11 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
avoir_description: $scope.description
},
function (_wallet) {
growl.success(_t('wallet_credit_successfully'));
growl.success(_t('app.shared.wallet.wallet_credit_successfully'));
return $uibModalInstance.close(_wallet);
},
function (error) {
growl.error(_t('a_problem_occurred_for_wallet_credit'));
growl.error(_t('app.shared.wallet.a_problem_occurred_for_wallet_credit'));
console.error(error);
}
);
@ -721,11 +750,12 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
{},
{ admin: $scope.admin },
function () {
growl.success(_t('administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) }, 'messageformat'));
growl.success(_t('app.admin.admins_new.administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) }));
return $state.go('app.admin.members');
}
, function (error) {
console.log(error);
growl.error(_t('app.admin.admins_new.failed_to_create_admin') + JSON.stringify(error.data ? error.data : error));
console.error(error);
}
);
};

View File

@ -37,12 +37,12 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
if (client.id != null) {
OpenAPIClient.update({ id: client.id }, { open_api_client: client }, function (clientResp) {
client = clientResp;
return growl.success(_t('client_successfully_updated'));
return growl.success(_t('app.admin.open_api_clients.client_successfully_updated'));
});
} else {
OpenAPIClient.save({ open_api_client: client }, function (client) {
$scope.clients.push(client);
return growl.success(_t('client_successfully_created'));
return growl.success(_t('app.admin.open_api_clients.client_successfully_created'));
});
}
@ -61,8 +61,8 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_open_api_client')
title: _t('app.admin.open_api_clients.confirmation_required'),
msg: _t('app.admin.open_api_clients.do_you_really_want_to_delete_this_open_api_client')
};
}
}
@ -70,7 +70,7 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
, () =>
OpenAPIClient.delete({ id: $scope.clients[index].id }, function () {
$scope.clients.splice(index, 1);
return growl.success(_t('client_successfully_deleted'));
return growl.success(_t('app.admin.open_api_clients.client_successfully_deleted'));
})
);
@ -79,8 +79,8 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_revoke_this_open_api_access')
title: _t('app.admin.open_api_clients.confirmation_required'),
msg: _t('app.admin.open_api_clients.do_you_really_want_to_revoke_this_open_api_access')
};
}
}
@ -88,7 +88,7 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
, () =>
OpenAPIClient.resetToken({ id: client.id }, {}, function (clientResp) {
client.token = clientResp.token;
return growl.success(_t('access_successfully_revoked'));
return growl.success(_t('app.admin.open_api_clients.access_successfully_revoked'));
})
);
}

View File

@ -121,7 +121,7 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
$uibModalInstance.close($scope.partner);
},
function (error) {
growl.error(_t('new_plan.unable_to_save_this_user_check_that_there_isnt_an_already_a_user_with_the_same_name'));
growl.error(_t('app.admin.plans.new.unable_to_save_this_user_check_that_there_isnt_an_already_a_user_with_the_same_name'));
console.error(error);
}
);
@ -143,9 +143,9 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
*/
$scope.afterSubmit = function (content) {
if ((content.id == null) && (content.plan_ids == null)) {
return growl.error(_t('new_plan.unable_to_create_the_subscription_please_try_again'));
return growl.error(_t('app.admin.plans.new.unable_to_create_the_subscription_please_try_again'));
} else {
growl.success(_t('new_plan.successfully_created_subscriptions_dont_forget_to_redefine_prices'));
growl.success(_t('app.admin.plans.new.successfully_created_subscriptions_dont_forget_to_redefine_prices'));
if (content.plan_ids != null) {
return $state.go('app.admin.pricing');
} else {
@ -237,9 +237,9 @@ Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'p
*/
$scope.afterSubmit = function (content) {
if ((content.id == null) && (content.plan_ids == null)) {
return growl.error(_t('edit_plan.unable_to_save_subscription_changes_please_try_again'));
return growl.error(_t('app.admin.plans.edit.unable_to_save_subscription_changes_please_try_again'));
} else {
growl.success(_t('edit_plan.subscription_successfully_changed'));
growl.success(_t('app.admin.plans.edit.subscription_successfully_changed'));
return $state.go('app.admin.pricing');
}
};

View File

@ -108,7 +108,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
if (data != null) {
return TrainingsPricing.update({ id: trainingsPricing.id }, { trainings_pricing: { amount: data } }).$promise;
} else {
return _t('pricing.please_specify_a_number');
return _t('app.admin.pricing.please_specify_a_number');
}
};
@ -146,7 +146,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
*/
$scope.showTrainings = function (trainings) {
if (!angular.isArray(trainings) || !(trainings.length > 0)) {
return _t('pricing.none');
return _t('app.admin.pricing.none');
}
const selected = [];
@ -155,7 +155,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return selected.push(t.name);
}
});
if (selected.length) { return selected.join(' | '); } else { return _t('pricing.none'); }
if (selected.length) { return selected.join(' | '); } else { return _t('app.admin.pricing.none'); }
};
/**
@ -170,7 +170,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
{ training_credit_nb: newdata.training_credits }
, angular.noop() // do nothing in case of success
, function (error) {
growl.error(_t('pricing.an_error_occurred_while_saving_the_number_of_credits'));
growl.error(_t('app.admin.pricing.an_error_occurred_while_saving_the_number_of_credits'));
console.error(error);
}
);
@ -190,11 +190,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return $scope.trainingCreditsGroups[planId].splice($scope.trainingCreditsGroups[planId].indexOf(tc.id), 1);
}
, function (error) {
growl.error(_t('pricing.an_error_occurred_while_deleting_credit_with_the_TRAINING', { TRAINING: tc.creditable.name }));
growl.error(_t('app.admin.pricing.an_error_occurred_while_deleting_credit_with_the_TRAINING', { TRAINING: tc.creditable.name }));
console.error(error);
});
} else {
return growl.error(_t('pricing.an_error_occurred_unable_to_find_the_credit_to_revoke'));
return growl.error(_t('app.admin.pricing.an_error_occurred_unable_to_find_the_credit_to_revoke'));
}
}
});
@ -215,7 +215,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
, function (error) { // failed
const training = getTrainingFromId(newTrainingId);
growl.error(_t('pricing.an_error_occurred_while_creating_credit_with_the_TRAINING', { TRAINING: training.name }));
growl.error(_t('app.admin.pricing.an_error_occurred_while_creating_credit_with_the_TRAINING', { TRAINING: training.name }));
return console.error(error);
});
}
@ -250,7 +250,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
* @returns {String}
*/
$scope.showCreditableName = function (credit) {
let selected = _t('pricing.not_set');
let selected = _t('app.admin.pricing.not_set');
if (credit && credit.creditable_id) {
const object = $scope.getCreditable(credit);
selected = object.name;
@ -295,7 +295,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
$scope.saveMachineCredit = function (data, id) {
for (let mc of Array.from($scope.machineCredits)) {
if ((mc.plan_id === data.plan_id) && (mc.creditable_id === data.creditable_id) && ((id === null) || (mc.id !== id))) {
growl.error(_t('pricing.error_a_credit_linking_this_machine_with_that_subscription_already_exists'));
growl.error(_t('app.admin.pricing.error_a_credit_linking_this_machine_with_that_subscription_already_exists'));
if (!id) {
$scope.machineCredits.pop();
}
@ -304,18 +304,18 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
if (id != null) {
return Credit.update({ id }, { credit: data }, function () { growl.success(_t('pricing.changes_have_been_successfully_saved')); });
return Credit.update({ id }, { credit: data }, function () { growl.success(_t('app.admin.pricing.changes_have_been_successfully_saved')); });
} else {
data.creditable_type = 'Machine';
return Credit.save(
{ credit: data }
, function (resp) {
$scope.machineCredits[$scope.machineCredits.length - 1].id = resp.id;
return growl.success(_t('pricing.credit_was_successfully_saved'));
return growl.success(_t('app.admin.pricing.credit_was_successfully_saved'));
}
, function (err) {
$scope.machineCredits.pop();
growl.error(_t('pricing.error_creating_credit'));
growl.error(_t('app.admin.pricing.error_creating_credit'));
console.error(err);
});
}
@ -365,7 +365,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
$scope.saveSpaceCredit = function (data, id) {
for (let sc of Array.from($scope.spaceCredits)) {
if ((sc.plan_id === data.plan_id) && (sc.creditable_id === data.creditable_id) && ((id === null) || (sc.id !== id))) {
growl.error(_t('pricing.error_a_credit_linking_this_space_with_that_subscription_already_exists'));
growl.error(_t('app.admin.pricing.error_a_credit_linking_this_space_with_that_subscription_already_exists'));
if (!id) {
$scope.spaceCredits.pop();
}
@ -374,18 +374,18 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
if (id != null) {
return Credit.update({ id }, { credit: data }, function () { growl.success(_t('pricing.changes_have_been_successfully_saved')); });
return Credit.update({ id }, { credit: data }, function () { growl.success(_t('app.admin.pricing.changes_have_been_successfully_saved')); });
} else {
data.creditable_type = 'Space';
return Credit.save(
{ credit: data }
, function (resp) {
$scope.spaceCredits[$scope.spaceCredits.length - 1].id = resp.id;
return growl.success(_t('pricing.credit_was_successfully_saved'));
return growl.success(_t('app.admin.pricing.credit_was_successfully_saved'));
}
, function (err) {
$scope.spaceCredits.pop();
return growl.error(_t('pricing.error_creating_credit'));
return growl.error(_t('app.admin.pricing.error_creating_credit'));
});
}
};
@ -419,8 +419,8 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
*/
$scope.getPlanType = function (type) {
if (type === 'PartnerPlan') {
return _t('pricing.partner');
} else { return _t('pricing.standard'); }
return _t('app.admin.pricing.partner');
} else { return _t('app.admin.pricing.standard'); }
};
/**
@ -453,7 +453,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
if (data != null) {
return Price.update({ id: price.id }, { price: { amount: data } }).$promise;
} else {
return _t('pricing.please_specify_a_number');
return _t('app.admin.pricing.please_specify_a_number');
}
};
@ -471,8 +471,8 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
resolve: {
object () {
return {
title: _t('pricing.confirmation_required'),
msg: _t('pricing.do_you_really_want_to_delete_this_subscription_plan')
title: _t('app.admin.pricing.confirmation_required'),
msg: _t('app.admin.pricing.do_you_really_want_to_delete_this_subscription_plan')
};
}
}
@ -482,12 +482,12 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
Plan.delete(
{ id },
function (res) {
growl.success(_t('pricing.subscription_plan_was_successfully_deleted'));
growl.success(_t('app.admin.pricing.subscription_plan_was_successfully_deleted'));
return $scope.plans.splice(findItemIdxById(plans, id), 1);
},
function (error) {
if (error.statusText) { console.error(`[EditPricingController::deletePlan] Error: ${error.statusText}`); }
growl.error(_t('pricing.unable_to_delete_the_specified_subscription_an_error_occurred'));
growl.error(_t('app.admin.pricing.unable_to_delete_the_specified_subscription_an_error_occurred'));
}
);
}
@ -519,8 +519,8 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
resolve: {
object () {
return {
title: _t('pricing.confirmation_required'),
msg: _t('pricing.do_you_really_want_to_delete_this_coupon')
title: _t('app.admin.pricing.confirmation_required'),
msg: _t('app.admin.pricing.do_you_really_want_to_delete_this_coupon')
};
}
}
@ -528,16 +528,16 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
, function () {
// the admin has confirmed, delete the coupon
Coupon.delete({ id }, function (res) {
growl.success(_t('coupon_was_successfully_deleted'));
growl.success(_t('app.admin.pricing.coupon_was_successfully_deleted'));
return $scope.coupons.splice(findItemIdxById(coupons, id), 1);
}
, function (error) {
if (error.statusText) { console.error(`[EditPricingController::deleteCoupon] Error: ${error.statusText}`); }
if (error.status === 422) {
return growl.error(_t('pricing.unable_to_delete_the_specified_coupon_already_in_use'));
return growl.error(_t('app.admin.pricing.unable_to_delete_the_specified_coupon_already_in_use'));
} else {
return growl.error(_t('pricing.unable_to_delete_the_specified_coupon_an_unexpected_error_occurred'));
return growl.error(_t('app.admin.pricing.unable_to_delete_the_specified_coupon_an_unexpected_error_occurred'));
}
});
});
@ -566,11 +566,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
// Callback to validate sending of the coupon
$scope.ok = function () {
Coupon.send({ coupon_code: coupon.code, user_id: $scope.ctrl.member.id }, function (res) {
growl.success(_t('pricing.coupon_successfully_sent_to_USER', { USER: $scope.ctrl.member.name }));
growl.success(_t('app.admin.pricing.coupon_successfully_sent_to_USER', { USER: $scope.ctrl.member.name }));
return $uibModalInstance.close({ user_id: $scope.ctrl.member.id });
}
, function (err) {
growl.error(_t('pricing.an_error_occurred_unable_to_send_the_coupon'));
growl.error(_t('app.admin.pricing.an_error_occurred_unable_to_send_the_coupon'));
console.error(err);
});
};

View File

@ -166,7 +166,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
Setting.update(
{ name: setting.name },
{ value },
function () { growl.success(_t('settings.customization_of_SETTING_successfully_saved', { SETTING: _t(`settings.${setting.name}`) })); },
function () { growl.success(_t('app.admin.settings.customization_of_SETTING_successfully_saved', { SETTING: _t(`app.admin.settings.${setting.name}`) })); },
function (error) { console.log(error); }
);
};
@ -191,7 +191,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
// reset history
$scope.privacyDraftsHistory = [];
data.setting.history.forEach(function (draft) {
$scope.privacyDraftsHistory.push({ id: draft.id, name: _t('settings.privacy.draft_from_USER_DATE', { USER: draft.user.name, DATE: draft.created_at }), content: draft.value });
$scope.privacyDraftsHistory.push({ id: draft.id, name: _t('app.admin.settings.privacy.draft_from_USER_DATE', { USER: draft.user.name, DATE: draft.created_at }), content: draft.value });
});
if (type === 'privacy_draft') {
const orderedHistory = $filter('orderBy')(data.setting.history, 'created_at');
@ -219,7 +219,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
angular.forEach(v, function(err) { growl.error(err); })
});
} else {
growl.success(_t('settings.file_successfully_updated'));
growl.success(_t('app.admin.settings.file_successfully_updated'));
if (content.custom_asset.name === 'cgu-file') {
$scope.cguFile = content.custom_asset;
$scope.methods.cgu = 'put';
@ -316,7 +316,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
}
privacyDraftsPromise.setting.history.forEach(function (draft) {
$scope.privacyDraftsHistory.push({ id: draft.id, name: _t('settings.privacy.draft_from_USER_DATE', { USER: draft.user.name, DATE: moment(draft.created_at).format('L LT') }), content: draft.value });
$scope.privacyDraftsHistory.push({ id: draft.id, name: _t('app.admin.settings.privacy.draft_from_USER_DATE', { USER: draft.user.name, DATE: moment(draft.created_at).format('L LT') }), content: draft.value });
});
};
@ -347,7 +347,7 @@ Application.Controllers.controller('SavePolicyController', ['$scope', '$uibModal
*/
$scope.publish = function () {
saveCb({ name: 'privacy_body', value: privacyPolicy.bodyTemp });
growl.info(_t('settings.privacy.users_notified'));
growl.info(_t('app.admin.settings.privacy.users_notified'));
$uibModalInstance.close('privacy_body');
};
/**

View File

@ -130,6 +130,13 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
};
/**
* Return a localized name for the given field
*/
$scope.customFieldName = function (field) {
return _t(`app.admin.statistics.${field}`);
}
/**
* Callback to open the datepicker (interval start)
* @param $event {Object} jQuery event object
@ -215,10 +222,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
*/
$scope.formatSex = function (sex) {
if (sex === 'male') {
return _t('man');
return _t('app.admin.statistics.man');
}
if (sex === 'female') {
return _t('woman');
return _t('app.admin.statistics.woman');
}
};
@ -397,7 +404,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
};
/**
* Run the elasticSearch query to retreive the /stats/type aggregations
* Run the elasticSearch query to retrieve the /stats/type aggregations
* @param index {String} elasticSearch document type (account|event|machine|project|subscription|training)
* @param type {String} statistics type (month|year|booking|hour|user|project)
* @param custom {{key:{string}, value:{string}}|null} custom filter property or null to disable this filter
@ -552,17 +559,17 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
};
/**
* Fullfil the list of available options in the custom filter panel. The list will be based on common
* Fulfill the list of available options in the custom filter panel. The list will be based on common
* properties and on index-specific properties (additional_fields)
*/
var buildCustomFiltersList = function () {
$scope.filters = [
{ key: 'date', label: _t('date'), values: ['input_date'] },
{ key: 'userId', label: _t('user_id'), values: ['input_number'] },
{ key: 'gender', label: _t('gender'), values: [{ key: 'male', label: _t('man') }, { key: 'female', label: _t('woman') }] },
{ key: 'age', label: _t('age'), values: ['input_number'] },
{ key: 'subType', label: _t('type'), values: $scope.type.active.subtypes },
{ key: 'ca', label: _t('revenue'), values: ['input_number'] }
{ key: 'date', label: _t('app.admin.statistics.date'), values: ['input_date'] },
{ key: 'userId', label: _t('app.admin.statistics.user_id'), values: ['input_number'] },
{ key: 'gender', label: _t('app.admin.statistics.gender'), values: [{ key: 'male', label: _t('app.admin.statistics.man') }, { key: 'female', label: _t('app.admin.statistics.woman') }] },
{ key: 'age', label: _t('app.admin.statistics.age'), values: ['input_number'] },
{ key: 'subType', label: _t('app.admin.statistics.type'), values: $scope.type.active.subtypes },
{ key: 'ca', label: _t('app.admin.statistics.revenue'), values: ['input_number'] }
];
if (!$scope.type.active.simple) {
@ -709,7 +716,7 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u
Export.status(statusQry).then(function (res) {
if (!res.data.exists) {
return growl.success(_t('export_is_running_you_ll_be_notified_when_its_ready'));
return growl.success(_t('app.admin.statistics.export_is_running_you_ll_be_notified_when_its_ready'));
}
});

View File

@ -11,7 +11,7 @@
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
Application.Controllers.controller('TagsController', ['$scope', 'tagsPromise', 'Tag', 'growl', '_t', function ($scope, tagsPromise, Tag, growl, _t) {
Application.Controllers.controller('TagsController', ['$scope', 'tagsPromise', 'Tag', 'dialogs', 'growl', '_t', function ($scope, tagsPromise, Tag, dialogs, growl, _t) {
// List of users's tags
$scope.tags = tagsPromise;
@ -44,15 +44,15 @@ Application.Controllers.controller('TagsController', ['$scope', 'tagsPromise', '
*/
$scope.saveTag = function (data, id) {
if (id != null) {
return Tag.update({ id }, { tag: data }, response => growl.success(_t('changes_successfully_saved'))
, error => growl.error(_t('an_error_occurred_while_saving_changes')));
return Tag.update({ id }, { tag: data }, response => growl.success(_t('app.admin.members.tag_form.changes_successfully_saved'))
, error => growl.error(_t('app.admin.members.tag_form.an_error_occurred_while_saving_changes')));
} else {
return Tag.save({ tag: data }, function (resp) {
growl.success(_t('new_tag_successfully_saved'));
growl.success(_t('app.admin.members.tag_form.new_tag_successfully_saved'));
return $scope.tags[$scope.tags.length - 1].id = resp.id;
}
, function (error) {
growl.error(_t('an_error_occurred_while_saving_the_new_tag'));
growl.error(_t('app.admin.members.tag_form.an_error_occurred_while_saving_the_new_tag'));
return $scope.tags.splice($scope.tags.length - 1, 1);
});
}
@ -62,13 +62,24 @@ Application.Controllers.controller('TagsController', ['$scope', 'tagsPromise', '
* Deletes the tag at the specified index
* @param index {number} tag index in the $scope.tags array
*/
return $scope.removeTag = index =>
// TODO add confirmation : les utilisateurs seront déasociés
$scope.removeTag = index =>
dialogs.confirm({
resolve: {
object () {
return {
title: _t('app.admin.members.tag_form.confirmation_required'),
msg: _t('app.admin.members.tag_form.confirm_delete_tag_html')
};
}
}
}
, () => {
Tag.delete({ id: $scope.tags[index].id }, function (resp) {
growl.success(_t('tag_successfully_deleted'));
growl.success(_t('app.admin.members.tag_form.tag_successfully_deleted'));
return $scope.tags.splice(index, 1);
}
, error => growl.error(_t('an_error_occurred_and_the_tag_deletion_failed')));
, error => growl.error(_t('app.admin.members.tag_form.an_error_occurred_and_the_tag_deletion_failed')));
});
}
]);

View File

@ -196,7 +196,7 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
return selected.push(m.name);
}
});
if (selected.length) { return selected.join(', '); } else { return _t('none'); }
if (selected.length) { return selected.join(', '); } else { return _t('app.admin.trainings.none'); }
};
/**
@ -276,8 +276,8 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_training')
title: _t('app.admin.trainings.confirmation_required'),
msg: _t('app.admin.trainings.do_you_really_want_to_delete_this_training')
};
}
}
@ -285,10 +285,10 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
function () { // deletion confirmed
training.$delete(function () {
$scope.trainings.splice(index, 1);
growl.info(_t('training_successfully_deleted'));
growl.info(_t('app.admin.trainings.training_successfully_deleted'));
},
function (error) {
growl.warning(_t('unable_to_delete_the_training_because_some_users_alredy_booked_it'));
growl.warning(_t('app.admin.trainings.unable_to_delete_the_training_because_some_users_already_booked_it'));
console.error(error);
});
}

View File

@ -149,7 +149,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
$uibModalInstance.close(user);
} else {
// the user was not saved in database, something wrong occurred
growl.error(_t('unexpected_error_occurred'));
growl.error(_t('app.public.common.unexpected_error_occurred'));
}
}, function (error) {
// creation failed...
@ -204,7 +204,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
};
}]
}).result['finally'](null).then(function () {
growl.success(_t('your_password_was_successfully_changed'));
growl.success(_t('app.public.common.your_password_was_successfully_changed'));
return Auth.login().then(function (user) {
$scope.setCurrentUser(user);
}, function (error) {
@ -325,7 +325,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
angular.forEach(notifications.notifications, function (n) { toDisplay.push(n); });
if (toDisplay.length < notifications.totals.unread) {
toDisplay.push({ message: { description: _t('and_NUMBER_other_notifications', { NUMBER: notifications.totals.unread - toDisplay.length }, 'messageformat') } });
toDisplay.push({ message: { description: _t('app.public.common.and_NUMBER_other_notifications', { NUMBER: notifications.totals.unread - toDisplay.length }) } });
}
angular.forEach(toDisplay, function (notification) { growl.info(notification.message.description); });
@ -375,7 +375,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
console.error(`Authentication failed: ${JSON.stringify(error)}`);
$scope.alerts = [];
return $scope.alerts.push({
msg: _t('wrong_email_or_password'),
msg: _t('app.public.common.wrong_email_or_password'),
type: 'danger'
});
});
@ -418,13 +418,13 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
$scope.alerts = [];
return $http.post('/users/password.json', { user: $scope.user }).then(function () { $uibModalInstance.close(); }).catch(function () {
$scope.alerts.push({
msg: _t('your_email_address_is_unknown'),
msg: _t('app.public.common.your_email_address_is_unknown'),
type: 'danger'
});
});
};
}]
}).result['finally'](null).then(function () { growl.info(_t('you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password')); });
}).result['finally'](null).then(function () { growl.info(_t('app.public.common.you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password')); });
}
});
// otherwise the user just closed the modal

View File

@ -16,8 +16,8 @@
* Controller used in the public calendar global
*/
Application.Controllers.controller('CalendarController', ['$scope', '$state', '$aside', 'moment', 'Availability', 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise', 'spacesPromise',
function ($scope, $state, $aside, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise) {
Application.Controllers.controller('CalendarController', ['$scope', '$state', '$aside', 'moment', 'Availability', 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise', 'spacesPromise', 'iCalendarPromise',
function ($scope, $state, $aside, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise, iCalendarPromise) {
/* PRIVATE STATIC CONSTANTS */
let currentMachineEvent = null;
machinesPromise.forEach(m => m.checked = true);
@ -38,6 +38,9 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
// List of spaces
$scope.spaces = spacesPromise.filter(t => !t.disabled);
// List of external iCalendar sources
$scope.externals = iCalendarPromise.map(e => Object.assign(e, { checked: true }));
// add availabilities source to event sources
$scope.eventSources = [];
@ -48,10 +51,41 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
trainings: isSelectAll('trainings', scope),
machines: isSelectAll('machines', scope),
spaces: isSelectAll('spaces', scope),
externals: isSelectAll('externals', scope),
evt: filter.evt,
dispo: filter.dispo
});
return $scope.calendarConfig.events = availabilitySourceUrl();
$scope.calendarConfig.events = availabilitySourceUrl();
// external iCalendar events sources
$scope.externals.forEach(e => {
if (e.checked) {
if (!$scope.eventSources.some(evt => evt.id === e.id)) {
$scope.eventSources.push({
id: e.id,
url: `/api/i_calendar/${e.id}/events`,
textColor: e.text_color || '#000',
color: e.color
});
}
} else {
if ($scope.eventSources.some(evt => evt.id === e.id)) {
const idx = $scope.eventSources.findIndex(evt => evt.id === e.id);
$scope.eventSources.splice(idx, 1);
}
}
});
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEventSources');
};
/**
* Return a CSS-like style of the given calendar configuration
* @param calendar
*/
$scope.calendarStyle = function (calendar) {
return {
'border-color': calendar.color,
'color': calendar.text_color
};
};
// a variable for formation/machine/event/dispo checkbox is or not checked
@ -59,6 +93,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
trainings: isSelectAll('trainings', $scope),
machines: isSelectAll('machines', $scope),
spaces: isSelectAll('spaces', $scope),
externals: isSelectAll('externals', $scope),
evt: true,
dispo: true
};
@ -85,6 +120,9 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
spaces () {
return $scope.spaces;
},
externals () {
return $scope.externals;
},
filter () {
return $scope.filter;
},
@ -95,10 +133,11 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
return $scope.filterAvailabilities;
}
},
controller: ['$scope', '$uibModalInstance', 'trainings', 'machines', 'spaces', 'filter', 'toggleFilter', 'filterAvailabilities', function ($scope, $uibModalInstance, trainings, machines, spaces, filter, toggleFilter, filterAvailabilities) {
controller: ['$scope', '$uibModalInstance', 'trainings', 'machines', 'spaces', 'externals', 'filter', 'toggleFilter', 'filterAvailabilities', function ($scope, $uibModalInstance, trainings, machines, spaces, externals, filter, toggleFilter, filterAvailabilities) {
$scope.trainings = trainings;
$scope.machines = machines;
$scope.spaces = spaces;
$scope.externals = externals;
$scope.filter = filter;
$scope.toggleFilter = (type, filter) => toggleFilter(type, filter);
@ -114,25 +153,66 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
/* PRIVATE SCOPE */
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = () => {
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
events: availabilitySourceUrl(),
slotEventOverlap: true,
header: {
left: 'month agendaWeek agendaDay',
center: 'title',
right: 'today prev,next'
},
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss')),
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss')),
defaultView: window.innerWidth <= 480 ? 'agendaDay' : 'agendaWeek',
eventClick (event, jsEvent, view) {
return calendarEventClickCb(event, jsEvent, view);
},
viewRender (view, element) {
return viewRenderCb(view, element);
},
eventRender (event, element, view) {
return eventRenderCb(event, element);
}
});
$scope.externals.forEach(e => {
if (e.checked) {
$scope.eventSources.push({
id: e.id,
url: `/api/i_calendar/${e.id}/events`,
textColor: e.text_color || '#000',
color: e.color
});
}
});
};
/**
* Callback triggered when an event object is clicked in the fullCalendar view
*/
const calendarEventClickCb = function (event, jsEvent, view) {
// current calendar object
const { calendar } = uiCalendarConfig.calendars;
if (event.available_type === 'machines') {
currentMachineEvent = event;
calendar.fullCalendar('changeView', 'agendaDay');
return calendar.fullCalendar('gotoDate', event.start);
calendar.fullCalendar('gotoDate', event.start);
} else if (event.available_type === 'space') {
calendar.fullCalendar('changeView', 'agendaDay');
return calendar.fullCalendar('gotoDate', event.start);
calendar.fullCalendar('gotoDate', event.start);
} else if (event.available_type === 'event') {
return $state.go('app.public.events_show', { id: event.event_id });
$state.go('app.public.events_show', { id: event.event_id });
} else if (event.available_type === 'training') {
return $state.go('app.public.training_show', { id: event.training_id });
$state.go('app.public.training_show', { id: event.training_id });
} else {
if (event.machine_id) {
return $state.go('app.public.machines_show', { id: event.machine_id });
$state.go('app.public.machines_show', { id: event.machine_id });
} else if (event.space_id) {
return $state.go('app.public.space_show', { id: event.space_id });
$state.go('app.public.space_show', { id: event.space_id });
}
}
};
@ -156,7 +236,10 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
}
};
// function is called when calendar view is rendered or changed
/**
* This function is called when calendar view is rendered or changed
* @see https://fullcalendar.io/docs/v3/viewRender#v2
*/
const viewRenderCb = function (view, element) {
toggleSlotEventOverlap(view);
if (view.type === 'agendaDay') {
@ -165,8 +248,12 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
}
};
/**
* Callback triggered by fullCalendar when it is about to render an event.
* @see https://fullcalendar.io/docs/v3/eventRender#v2
*/
const eventRenderCb = function (event, element) {
if (event.tags.length > 0) {
if (event.tags && event.tags.length > 0) {
let html = '';
for (let tag of Array.from(event.tags)) {
html += `<span class='label label-success text-white'>${tag.name}</span> `;
@ -184,30 +271,6 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
var availabilitySourceUrl = () => `/api/availabilities/public?${$.param(getFilter())}`;
const initialize = () =>
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
events: availabilitySourceUrl(),
slotEventOverlap: true,
header: {
left: 'month agendaWeek agendaDay',
center: 'title',
right: 'today prev,next'
},
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss')),
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss')),
defaultView: window.innerWidth <= 480 ? 'agendaDay' : 'agendaWeek',
eventClick (event, jsEvent, view) {
return calendarEventClickCb(event, jsEvent, view);
},
viewRender (view, element) {
return viewRenderCb(view, element);
},
eventRender (event, element, view) {
return eventRenderCb(event, element);
}
});
// !!! MUST BE CALLED AT THE END of the controller
return initialize();
}

View File

@ -126,13 +126,16 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
}
]);
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$stateParams', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'growl', '_t', 'Wallet', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise',
function ($scope, $state, $stateParams, $rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, growl, _t, Wallet, helpers, dialogs, priceCategoriesPromise, settingsPromise) {
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$stateParams', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'Slot', 'eventPromise', 'growl', '_t', 'Wallet', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise',
function ($scope, $state, $stateParams, $rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, Slot, eventPromise, growl, _t, Wallet, helpers, dialogs, priceCategoriesPromise, settingsPromise) {
/* PUBLIC SCOPE */
// reservations for the currently shown event
$scope.reservations = [];
// current date & time
$scope.now = moment();
// user to deal with
$scope.ctrl =
{ member: {} };
@ -166,6 +169,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// Global config: delay in hours before a booking while changing the booking slot is forbidden
$scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay);
// Global config: is the user authorized to cancel his booking slots?
$scope.enableBookingCancel = settingsPromise.booking_cancel_enable === 'true';
// Global config: delay in hours from now when restrictions occurs about cancelling reservations
$scope.cancelBookingDelay = parseInt(settingsPromise.booking_cancel_delay);
// Message displayed to the end user about rules that applies to events reservations
$scope.eventExplicationsAlert = settingsPromise.event_explications_alert;
@ -178,8 +187,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_event')
title: _t('app.public.events_show.confirmation_required'),
msg: _t('app.public.events_show.do_you_really_want_to_delete_this_event')
};
}
}
@ -187,10 +196,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// the admin has confirmed, delete
event.$delete(function () {
$state.go('app.public.events_list');
return growl.info(_t('event_successfully_deleted'));
return growl.info(_t('app.public.events_show.event_successfully_deleted'));
}, function (error) {
console.error(error);
growl.error(_t('unable_to_delete_the_event_because_some_users_alredy_booked_it'));
growl.error(_t('app.public.events_show.unable_to_delete_the_event_because_some_users_alredy_booked_it'));
});
}
);
@ -279,7 +288,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
const amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount);
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('online_payment_disabled'));
growl.error(_t('app.public.events_show.online_payment_disabled'));
} else {
return payByStripe(reservation);
}
@ -291,7 +300,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
});
} else {
// otherwise we alert, this error musn't occur when the current user is not admin
return growl.error(_t('please_select_a_member_first'));
return growl.error(_t('app.public.events_show.please_select_a_member_first'));
}
};
@ -341,16 +350,50 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
});
};
/**
* Callback to cancel a reservation
* @param reservation {{id:number, reservable_id:number, nb_reserve_places:number}}
*/
$scope.cancelReservation = function(reservation) {
dialogs.confirm({
resolve: {
object: function() {
return {
title: _t('app.public.events_show.cancel_the_reservation'),
msg: _t('app.public.events_show.do_you_really_want_to_cancel_this_reservation_this_apply_to_all_booked_tickets')
};
}
}
}, function() { // cancel confirmed
Slot.cancel({
id: reservation.slots[0].id
}, function() { // successfully canceled
let index;
growl.success(_t('app.public.events_show.reservation_was_successfully_cancelled'));
index = $scope.reservations.indexOf(reservation);
$scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats;
$scope.reservations[index].slots[0].canceled_at = new Date();
}, function(error) {
growl.warning(_t('app.public.events_show.cancellation_failed'));
});
});
};
/**
* Test if the provided reservation has been cancelled
* @param reservation {Reservation}
* @returns {boolean}
*/
$scope.isCancelled = function(reservation) {
return !!(reservation.slots[0].canceled_at);
}
/**
* Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose
* a new date for his reservation (if any available)
* @param reservation {{id:number, reservable_id:number, nb_reserve_places:number}}
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.modifyReservation = function (reservation, e) {
e.preventDefault();
e.stopPropagation();
$scope.modifyReservation = function (reservation) {
const index = $scope.reservations.indexOf(reservation);
return $uibModal.open({
templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>',
@ -364,9 +407,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
$scope.reservation = angular.copy(reservation);
// set the reservable_id to the first available event
for (e of Array.from(event.recurrence_events)) {
if (e.nb_free_places > reservation.total_booked_seats) {
$scope.reservation.reservable_id = e.id;
for (evt of Array.from(event.recurrence_events)) {
if (evt.nb_free_places > reservation.total_booked_seats) {
$scope.reservation.reservable_id = evt.id;
break;
}
}
@ -419,7 +462,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
/**
* Checks if the provided reservation is able to be moved (date change)
* @param reservation {{total_booked_seats:number}}
* @param reservation {{slots:[], total_booked_seats:number}}
*/
$scope.reservationCanModify = function (reservation) {
const slotStart = moment(reservation.slots[0].start_at);
@ -432,6 +475,17 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
return (isAble && $scope.enableBookingMove && (slotStart.diff(now, 'hours') >= $scope.moveBookingDelay));
};
/**
* Checks if the provided reservation is able to be cancelled
* @param reservation {{slots:[]}}
*/
$scope.reservationCanCancel = function(reservation) {
var now, slotStart;
slotStart = moment(reservation.slots[0].start_at);
now = moment();
return $scope.enableBookingCancel && slotStart.diff(now, "hours") >= $scope.cancelBookingDelay;
};
/**
* Compute the total amount for the current reservation according to the previously set parameters
* and assign the result in $scope.reserve.amountTotal
@ -567,7 +621,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
};
/**
* Set the current reservation to the default values. This implies to reservation form to be hidden.
* Set the current reservation to the default values. This implies the reservation form to be hidden.
*/
var resetEventReserve = function () {
if ($scope.event) {
@ -696,12 +750,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// Button label
if ($scope.amount > 0) {
$scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) }, 'messageformat');
$scope.validButtonName = _t('app.public.events_show.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) });
} else {
if ((price.price > 0) && ($scope.walletAmount === 0)) {
$scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) }, 'messageformat');
$scope.validButtonName = _t('app.public.events_show.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) });
} else {
$scope.validButtonName = _t('confirm');
$scope.validButtonName = _t('app.shared.buttons.confirm');
}
}
@ -735,7 +789,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
/**
* What to do after the payment was successful
* @param resveration {Object} booked reservation
* @param reservation {Object} booked reservation
*/
var afterPayment = function (reservation) {
$scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats;

View File

@ -146,7 +146,7 @@ const _reserveMachine = function (machine, e) {
let text = '';
angular.forEach($scope.machine.trainings, function (training) {
if (text.length > 0) {
text += _this._t('machines_list._or_the_');
text += _this._t('app.public.machines_list._or_the_');
}
return text += training.name.substr(0, 1).toLowerCase() + training.name.substr(1);
});
@ -281,14 +281,14 @@ Application.Controllers.controller('ShowMachineController', ['$scope', '$state',
$scope.delete = function (machine) {
// check the permissions
if ($scope.currentUser.role !== 'admin') {
console.error(_t('unauthorized_operation'));
console.error(_t('app.public.machines_show.unauthorized_operation'));
} else {
dialogs.confirm({
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_machine')
title: _t('app.public.machines_show.confirmation_required'),
msg: _t('app.public.machines_show.do_you_really_want_to_delete_this_machine')
};
}
}
@ -297,7 +297,7 @@ Application.Controllers.controller('ShowMachineController', ['$scope', '$state',
// delete the machine then redirect to the machines listing
machine.$delete(
function () { $state.go('app.public.machines_list'); },
function (error) { growl.warning(_t('the_machine_cant_be_deleted_because_it_is_already_reserved_by_some_users')); console.error(error); }
function (error) { growl.warning(_t('app.public.machines_show.the_machine_cant_be_deleted_because_it_is_already_reserved_by_some_users')); console.error(error); }
);
});
}
@ -402,16 +402,16 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.machineExplicationsAlert = settingsPromise.machine_explications_alert;
/**
* 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 = FREE_SLOT_BORDER_COLOR;
$scope.selectedEvent.title = _t('i_reserve');
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_reserve');
return updateCalendar();
};
/**
* 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';
@ -431,16 +431,16 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.slotCancelled = function () { $scope.markSlotAsRemoved($scope.selectedEvent); };
/**
* Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
* Change the last selected slot's appearance to looks like 'currently looking for a new destination to exchange'
*/
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('i_change');
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_change');
return updateCalendar();
};
/**
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
* Change the last selected slot's appearance to looks like 'the slot being exchanged will take this place'
*/
$scope.changeModifyMachineSlot = function () {
if ($scope.events.placable) {
@ -449,7 +449,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('i_shift');
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_shift');
}
return updateCalendar();
};
@ -458,7 +458,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyMachineSlot = function () {
$scope.events.placable.title = $scope.currentUser.role !== 'admin' ? _t('i_ve_reserved') : _t('not_available');
$scope.events.placable.title = $scope.currentUser.role !== 'admin' ? _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;
@ -476,14 +476,14 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
};
/**
* Cancel the current booking modification, reseting the whole process
* Cancel the current booking modification, resetting the whole process
*/
$scope.cancelModifyMachineSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = '';
}
$scope.events.modifiable.title = $scope.currentUser.role !== 'admin' ? _t('i_ve_reserved') : _t('not_available');
$scope.events.modifiable.title = $scope.currentUser.role !== 'admin' ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
$scope.events.modifiable.backgroundColor = 'white';
return updateCalendar();
@ -500,7 +500,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
};
/**
* Changes the user current view from the plan subsription screen to the machine reservation agenda
* Changes the user current view from the plan subscription screen to the machine reservation agenda
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.doNotSubscribePlan = function (e) {
@ -539,11 +539,11 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
machineSlot.is_reserved = true;
machineSlot.can_modify = true;
if ($scope.currentUser.role !== 'admin') {
machineSlot.title = _t('i_ve_reserved');
machineSlot.title = _t('app.logged.machines_reserve.i_ve_reserved');
machineSlot.borderColor = BOOKED_SLOT_BORDER_COLOR;
updateMachineSlot(machineSlot, reservation, $scope.currentUser);
} else {
machineSlot.title = _t('not_available');
machineSlot.title = _t('app.logged.machines_reserve.not_available');
machineSlot.borderColor = UNAVAILABLE_SLOT_BORDER_COLOR;
updateMachineSlot(machineSlot, reservation, $scope.ctrl.member);
}
@ -595,7 +595,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
};
/**
* Triggered when fullCalendar tries to graphicaly render an event block.
* Triggered when fullCalendar tries to graphically render an event block.
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/

View File

@ -18,33 +18,33 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc
$scope.navLinks = [
{
state: 'app.public.home',
linkText: 'home',
linkText: 'app.public.common.home',
linkIcon: 'home'
},
{
state: 'app.public.machines_list',
linkText: 'reserve_a_machine',
linkText: 'app.public.common.reserve_a_machine',
linkIcon: 'cogs'
},
{
state: 'app.public.trainings_list',
linkText: 'trainings_registrations',
linkText: 'app.public.common.trainings_registrations',
linkIcon: 'graduation-cap'
},
{
state: 'app.public.events_list',
linkText: 'events_registrations',
linkText: 'app.public.common.events_registrations',
linkIcon: 'tags'
},
{
state: 'app.public.calendar',
linkText: 'public_calendar',
linkText: 'app.public.common.public_calendar',
linkIcon: 'calendar'
},
{
state: 'app.public.projects_list',
linkText: 'projects_gallery',
linkText: 'app.public.common.projects_gallery',
linkIcon: 'th'
}
@ -53,7 +53,7 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc
if (!Fablab.withoutPlans) {
$scope.navLinks.push({
state: 'app.public.plans',
linkText: 'subscriptions',
linkText: 'app.public.common.subscriptions',
linkIcon: 'credit-card'
});
}
@ -61,7 +61,7 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc
if (!Fablab.withoutSpaces) {
$scope.navLinks.splice(3, 0, {
state: 'app.public.spaces_list',
linkText: 'reserve_a_space',
linkText: 'app.public.common.reserve_a_space',
linkIcon: 'rocket'
});
}
@ -70,57 +70,57 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc
const adminNavLinks = [
{
state: 'app.admin.trainings',
linkText: 'trainings_monitoring',
linkText: 'app.public.common.trainings_monitoring',
linkIcon: 'graduation-cap'
},
{
state: 'app.admin.calendar',
linkText: 'manage_the_calendar',
linkText: 'app.public.common.manage_the_calendar',
linkIcon: 'calendar'
},
{
state: 'app.admin.members',
linkText: 'manage_the_users',
linkText: 'app.public.common.manage_the_users',
linkIcon: 'users'
},
{
state: 'app.admin.invoices',
linkText: 'manage_the_invoices',
linkText: 'app.public.common.manage_the_invoices',
linkIcon: 'file-pdf-o'
},
{
state: 'app.admin.pricing',
linkText: 'subscriptions_and_prices',
linkText: 'app.public.common.subscriptions_and_prices',
linkIcon: 'money'
},
{
state: 'app.admin.events',
linkText: 'manage_the_events',
linkText: 'app.public.common.manage_the_events',
linkIcon: 'tags'
},
{
state: 'app.public.machines_list',
linkText: 'manage_the_machines',
linkText: 'app.public.common.manage_the_machines',
linkIcon: 'cogs'
},
{
state: 'app.admin.project_elements',
linkText: 'manage_the_projects_elements',
linkText: 'app.public.common.manage_the_projects_elements',
linkIcon: 'tasks'
},
{
state: 'app.admin.statistics',
linkText: 'statistics',
linkText: 'app.public.common.statistics',
linkIcon: 'bar-chart-o'
},
{
state: 'app.admin.settings',
linkText: 'customization',
linkText: 'app.public.common.customization',
linkIcon: 'gear'
},
{
state: 'app.admin.open_api_clients',
linkText: 'open_api_clients',
linkText: 'app.public.common.open_api_clients',
linkIcon: 'cloud'
}
].concat(Fablab.adminNavLinks);
@ -130,7 +130,7 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc
if (!Fablab.withoutSpaces) {
return $scope.adminNavLinks.splice(7, 0, {
state: 'app.public.spaces_list',
linkText: 'manage_the_spaces',
linkText: 'app.public.common.manage_the_spaces',
linkIcon: 'rocket'
});
}

View File

@ -142,10 +142,10 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco
$rootScope.currentUser = user;
Auth._currentUser.group_id = user.group_id;
$scope.group.change = false;
return growl.success(_t('edit_profile.your_group_has_been_successfully_changed'));
return growl.success(_t('app.logged.dashboard.settings.your_group_has_been_successfully_changed'));
}
, function (err) {
growl.error(_t('edit_profile.an_unexpected_error_prevented_your_group_from_being_changed'));
growl.error(_t('app.logged.dashboard.settings.an_unexpected_error_prevented_your_group_from_being_changed'));
return console.error(err);
});
@ -198,13 +198,13 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco
resolve: {
object () {
return {
title: _t('confirmation_required'),
title: _t('app.logged.dashboard.settings.confirmation_required'),
msg: $sce.trustAsHtml(
_t('edit_profile.confirm_delete_your_account') + '<br/>' +
'<strong>' + _t('edit_profile.all_data_will_be_lost') + '</strong><br/><br/>' +
_t('edit_profile.invoicing_data_kept') + '<br/>' +
_t('edit_profile.statistic_data_anonymized') + '<br/>' +
_t('edit_profile.no_further_access_to_projects')
_t('app.logged.dashboard.settings.confirm_delete_your_account') + '<br/>' +
'<strong>' + _t('app.logged.dashboard.settings.all_data_will_be_lost') + '</strong><br/><br/>' +
_t('app.logged.dashboard.settings.invoicing_data_kept') + '<br/>' +
_t('app.logged.dashboard.settings.statistic_data_anonymized') + '<br/>' +
_t('app.logged.dashboard.settings.no_further_access_to_projects')
)
};
}
@ -214,12 +214,12 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco
Member.remove({ id: user.id }, () =>
Auth.logout().then(function () {
$state.go('app.public.home');
return growl.success(_t('edit_profile.your_user_account_has_been_successfully_deleted_goodbye'));
return growl.success(_t('app.logged.dashboard.settings.your_user_account_has_been_successfully_deleted_goodbye'));
})
, function (error) {
console.log(error);
return growl.error(_t('edit_profile.an_error_occured_preventing_your_account_from_being_deleted'));
return growl.error(_t('app.logged.dashboard.settings.an_error_occured_preventing_your_account_from_being_deleted'));
})
);

View File

@ -99,7 +99,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
const amountToPay = helpers.getAmountToPay($scope.cart.total, wallet.amount);
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('online_payment_disabled'));
growl.error(_t('app.public.plans.online_payment_disabled'));
} else {
return payByStripe();
}
@ -133,16 +133,16 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
if ($scope.currentUser.role !== 'admin') {
$rootScope.currentUser = user;
Auth._currentUser.group_id = user.group_id;
growl.success(_t('your_group_was_successfully_changed'));
growl.success(_t('app.public.plans.your_group_was_successfully_changed'));
} else {
growl.success(_t('the_user_s_group_was_successfully_changed'));
growl.success(_t('app.public.plans.the_user_s_group_was_successfully_changed'));
}
}
, function (err) {
if ($scope.currentUser.role !== 'admin') {
growl.error(_t('an_error_prevented_your_group_from_being_changed'));
growl.error(_t('app.public.plans.an_error_prevented_your_group_from_being_changed'));
} else {
growl.error(_t('an_error_prevented_to_change_the_user_s_group'));
growl.error(_t('app.public.plans.an_error_prevented_to_change_the_user_s_group'));
}
console.error(err);
});
@ -318,12 +318,12 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
// Button label
if ($scope.amount > 0) {
$scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) }, 'messageformat');
$scope.validButtonName = _t('app.public.plans.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) });
} else {
if ((price.price > 0) && ($scope.walletAmount === 0)) {
$scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) }, 'messageformat');
$scope.validButtonName = _t('app.public.plans.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) });
} else {
$scope.validButtonName = _t('confirm');
$scope.validButtonName = _t('app.shared.buttons.confirm');
}
}
@ -345,7 +345,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
, function (data, status) { // failed
$scope.alerts = [];
$scope.alerts.push({ msg: _t('an_error_occured_during_the_payment_process_please_try_again_later'), type: 'danger' });
$scope.alerts.push({ msg: _t('app.public.plans.an_error_occured_during_the_payment_process_please_try_again_later'), type: 'danger' });
$scope.attempting = false;
}
);

View File

@ -123,7 +123,7 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
if (err.data.error) {
growl.error(err.data.error);
} else {
growl.error(_t('an_unexpected_error_occurred_check_your_authentication_code'));
growl.error(_t('app.logged.profile_completion.an_unexpected_error_occurred_check_your_authentication_code'));
console.error(err);
}
});
@ -174,7 +174,7 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
function (email) {
// Request the server to send an auth-migration email to the current user
AuthProvider.send_code({ email },
function (res) { growl.info(_t('code_successfully_sent_again')); },
function (res) { growl.info(_t('app.logged.profile_completion.code_successfully_sent_again')); },
function (err) { growl.error(err.data.error); }
);
}

View File

@ -165,8 +165,8 @@ class ProjectsController {
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_step')
title: _t('app.shared.project.confirmation_required'),
msg: _t('app.shared.project.do_you_really_want_to_delete_this_step')
};
}
}
@ -215,6 +215,10 @@ class ProjectsController {
return false;
};
/**
* This function will query the API to autocomplete the typed user's name
* @param nameLookup {string}
*/
$scope.autoCompleteName = function (nameLookup) {
if (!nameLookup) {
return;
@ -246,6 +250,16 @@ class ProjectsController {
return step.project_step_images_attributes.splice(index, 1);
}
};
/**
* Returns the text to display on the save button, depending on the current state of the project
*/
$scope.saveButtonValue = function () {
if (!$scope.project.state || $scope.project.state === 'draft') {
return _t('app.shared.project.save_as_draft');
}
return _t('app.shared.buttons.save');
}
}
}
@ -324,7 +338,7 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
$scope.projectsPagination = new paginationService.Instance(OpenlabProject, currentPage, PROJECTS_PER_PAGE, null, { }, loadMoreOpenlabCallback);
return OpenlabProject.query({ q: $scope.search.q, page: currentPage, per_page: PROJECTS_PER_PAGE }, function (projectsPromise) {
if (projectsPromise.errors != null) {
growl.error(_t('projects_list.openlab_search_not_available_at_the_moment'));
growl.error(_t('app.public.projects_list.openlab_search_not_available_at_the_moment'));
$scope.openlab.searchOverWholeNetwork = false;
return $scope.triggerSearch();
} else {
@ -533,8 +547,8 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_project')
title: _t('app.public.projects_show.confirmation_required'),
msg: _t('app.public.projects_show.do_you_really_want_to_delete_this_project')
};
}
}
@ -543,7 +557,7 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
$scope.project.$delete(function () { $state.go('app.public.projects_list', {}, { reload: true }); });
});
} else {
return console.error(_t('unauthorized_operation'));
return console.error(_t('app.public.projects_show.unauthorized_operation'));
}
};
@ -577,12 +591,12 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
{ abuse: $scope.signaler },
function (res) {
// creation successful
growl.success(_t('your_report_was_successful_thanks'));
growl.success(_t('app.public.projects_show.your_report_was_successful_thanks'));
return $uibModalInstance.close(res);
}
, function (error) {
// creation failed...
growl.error(_t('an_error_occured_while_sending_your_report'));
growl.error(_t('app.public.projects_show.an_error_occured_while_sending_your_report'));
}
);
};

View File

@ -185,14 +185,14 @@ Application.Controllers.controller('ShowSpaceController', ['$scope', '$state', '
event.preventDefault();
// check the permissions
if ($scope.currentUser.role !== 'admin') {
return console.error(_t('space_show.unauthorized_operation'));
return console.error(_t('app.public.space_show.unauthorized_operation'));
} else {
return dialogs.confirm({
resolve: {
object () {
return {
title: _t('space_show.confirmation_required'),
msg: _t('space_show.do_you_really_want_to_delete_this_space')
title: _t('app.public.space_show.confirmation_required'),
msg: _t('app.public.space_show.do_you_really_want_to_delete_this_space')
};
}
}
@ -204,7 +204,7 @@ Application.Controllers.controller('ShowSpaceController', ['$scope', '$state', '
$state.go('app.public.spaces_list');
},
function (error) {
growl.warning(_t('space_show.the_space_cant_be_deleted_because_it_is_already_reserved_by_some_users'));
growl.warning(_t('app.public.space_show.the_space_cant_be_deleted_because_it_is_already_reserved_by_some_users'));
console.error(error);
}
);
@ -333,7 +333,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
*/
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('space_reserve.i_change');
$scope.selectedEvent.title = _t('app.logged.space_reserve.i_change');
return updateCalendar();
};
@ -347,7 +347,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('space_reserve.i_shift');
$scope.selectedEvent.title = _t('app.logged.space_reserve.i_shift');
}
return updateCalendar();
};
@ -356,7 +356,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyTrainingSlot = function () {
$scope.events.placable.title = _t('space_reserve.i_ve_reserved');
$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;
@ -382,7 +382,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = '';
}
$scope.events.modifiable.title = _t('space_reserve.i_ve_reserved');
$scope.events.modifiable.title = _t('app.logged.space_reserve.i_ve_reserved');
$scope.events.modifiable.backgroundColor = 'white';
return updateCalendar();
@ -451,7 +451,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
angular.forEach($scope.events.paid, function (spaceSlot, key) {
spaceSlot.is_reserved = true;
spaceSlot.can_modify = true;
spaceSlot.title = _t('space_reserve.i_ve_reserved');
spaceSlot.title = _t('app.logged.space_reserve.i_ve_reserved');
spaceSlot.backgroundColor = 'white';
spaceSlot.borderColor = RESERVED_SLOT_BORDER_COLOR;
return updateSpaceSlotId(spaceSlot, reservation);

View File

@ -47,15 +47,15 @@ Application.Controllers.controller('ShowTrainingController', ['$scope', '$state'
$scope.delete = function (training) {
// check the permissions
if ($scope.currentUser.role !== 'admin') {
console.error(_t('unauthorized_operation'));
growl.error(_t('app.public.training_show.unauthorized_operation'));
} else {
dialogs.confirm(
{
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_training')
title: _t('app.public.training_show.confirmation_required'),
msg: _t('app.public.training_show.do_you_really_want_to_delete_this_training')
};
}
}
@ -65,7 +65,7 @@ Application.Controllers.controller('ShowTrainingController', ['$scope', '$state'
training.$delete(
function () { $state.go('app.public.trainings_list'); },
function (error) {
growl.warning(_t('the_training_cant_be_deleted_because_it_is_already_reserved_by_some_users'));
growl.warning(_t('app.public.training_show.the_training_cant_be_deleted_because_it_is_already_reserved_by_some_users'));
console.error(error);
}
);
@ -208,7 +208,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
*/
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('i_change');
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('app.logged.trainings_reserve.i_change');
return updateCalendar();
};
@ -222,7 +222,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('i_shift');
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('app.logged.trainings_reserve.i_shift');
}
return updateCalendar();
};
@ -231,7 +231,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
* 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('i_ve_reserved') : $scope.events.placable.training.name;
$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;
@ -257,7 +257,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = $scope.events.placable.training.name;
}
$scope.events.modifiable.title = $scope.currentUser.role !== 'admin' ? $scope.events.modifiable.training.name + ' - ' + _t('i_ve_reserved') : $scope.events.modifiable.training.name;
$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();
@ -329,7 +329,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$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('i_ve_reserved');
$scope.events.paid[0].title = $scope.events.paid[0].training.name + ' - ' + _t('app.logged.trainings_reserve.i_ve_reserved');
if ($scope.selectedPlan) {
$scope.ctrl.member.subscribed_plan = angular.copy($scope.selectedPlan);

View File

@ -117,7 +117,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return $scope.modePlans = true;
} else {
// otherwise we alert, this error musn't occur when the current user hasn't the admin role
return growl.error(_t('cart.please_select_a_member_first'));
return growl.error(_t('app.shared.cart.please_select_a_member_first'));
}
};
@ -133,7 +133,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
if (!$scope.isAdmin() && (amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('cart.online_payment_disabled'));
growl.error(_t('app.shared.cart.online_payment_disabled'));
} else {
return payByStripe(reservation);
}
@ -145,7 +145,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
});
} else {
// otherwise we alert, this error musn't occur when the current user is not admin
return growl.error(_t('cart.please_select_a_member_first'));
return growl.error(_t('app.shared.cart.please_select_a_member_first'));
}
};
@ -173,7 +173,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return $scope.events.modifiable = null;
}
, function (err) { // failure
growl.error(_t('cart.unable_to_change_the_reservation'));
growl.error(_t('app.shared.cart.unable_to_change_the_reservation'));
return console.error(err);
});
};
@ -315,19 +315,19 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
resolve: {
object () {
return {
title: _t('cart.confirmation_required'),
msg: _t('cart.do_you_really_want_to_cancel_this_reservation')
title: _t('app.shared.cart.confirmation_required'),
msg: _t('app.shared.cart.do_you_really_want_to_cancel_this_reservation')
};
}
}
},
function () { // cancel confirmed
Slot.cancel({ id: $scope.slot.id }, function () { // successfully canceled
growl.success(_t('cart.reservation_was_cancelled_successfully'));
growl.success(_t('app.shared.cart.reservation_was_cancelled_successfully'));
if (typeof $scope.onSlotCancelSuccess === 'function') { return $scope.onSlotCancelSuccess(); }
}
, function () { // error while canceling
growl.error(_t('cart.cancellation_failed'));
growl.error(_t('app.shared.cart.cancellation_failed'));
});
}
);
@ -403,7 +403,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
});
} else {
// otherwise we alert, this error musn't occur when the current user is not admin
growl.warning(_t('cart.please_select_a_member_first'));
growl.warning(_t('app.shared.cart.please_select_a_member_first'));
return $scope.amountTotal = null;
}
};
@ -556,12 +556,12 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// Button label
if ($scope.amount > 0) {
$scope.validButtonName = _t('cart.confirm_payment_of_html', { ROLE: $rootScope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) }, 'messageformat');
$scope.validButtonName = _t('app.shared.cart.confirm_payment_of_html', { ROLE: $rootScope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) });
} else {
if ((price.price > 0) && ($scope.walletAmount === 0)) {
$scope.validButtonName = _t('cart.confirm_payment_of_html', { ROLE: $rootScope.currentUser.role, AMOUNT: $filter('currency')(price.price) }, 'messageformat');
$scope.validButtonName = _t('app.shared.cart.confirm_payment_of_html', { ROLE: $rootScope.currentUser.role, AMOUNT: $filter('currency')(price.price) });
} else {
$scope.validButtonName = _t('confirm');
$scope.validButtonName = _t('app.shared.buttons.confirm');
}
}
@ -576,7 +576,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
, function (response) {
$scope.alerts = [];
$scope.alerts.push({ msg: _t('cart.a_problem_occurred_during_the_payment_process_please_try_again_later'), type: 'danger' });
$scope.alerts.push({ msg: _t('app.shared.cart.a_problem_occurred_during_the_payment_process_please_try_again_later'), type: 'danger' });
return $scope.attempting = false;
});
};

View File

@ -53,15 +53,15 @@ Application.Directives.directive('coupon', [ '$rootScope', 'Coupon', '_t', funct
$scope.status = 'valid';
$scope.coupon = res;
if (res.type === 'percent_off') {
return $scope.messages.push({ type: 'success', message: _t('the_coupon_has_been_applied_you_get_PERCENT_discount', { PERCENT: res.percent_off }) });
return $scope.messages.push({ type: 'success', message: _t('app.shared.coupon_input.the_coupon_has_been_applied_you_get_PERCENT_discount', { PERCENT: res.percent_off }) });
} else {
return $scope.messages.push({ type: 'success', message: _t('the_coupon_has_been_applied_you_get_AMOUNT_CURRENCY', { AMOUNT: res.amount_off, CURRENCY: $rootScope.currencySymbol }) });
return $scope.messages.push({ type: 'success', message: _t('app.shared.coupon_input.the_coupon_has_been_applied_you_get_AMOUNT_CURRENCY', { AMOUNT: res.amount_off, CURRENCY: $rootScope.currencySymbol }) });
}
}
, function (err) {
$scope.status = 'invalid';
$scope.coupon = null;
return $scope.messages.push({ type: 'danger', message: _t(`unable_to_apply_the_coupon_because_${err.data.status}`) });
return $scope.messages.push({ type: 'danger', message: _t(`app.shared.coupon_input.unable_to_apply_the_coupon_because_${err.data.status}`) });
});
}
};

View File

@ -1,14 +1,3 @@
/* eslint-disable
no-return-assign,
no-undef,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
'use strict';
/**
@ -36,9 +25,9 @@ Application.Directives.directive('selectMember', [ 'Diacritics', 'Member', funct
q['subscription'] = attributes.subscription;
}
return Member.search(q, function (users) {
Member.search(q, function (users) {
scope.matchingMembers = users;
return scope.isLoadingMembers = false;
scope.isLoadingMembers = false;
}
, function (error) { console.error(error); });
};

View File

@ -73,7 +73,7 @@ Application.Directives.directive('stripeForm', ['Payment', 'growl', '_t',
if (response.error.statusText) {
growl.error(response.error.statusText);
} else {
growl.error(`${_t('payment_card_error')} ${response.error}`);
growl.error(`${_t('app.shared.messages.payment_card_error')} ${response.error}`);
}
confirmButton.prop('disabled', false);
} else if (response.requires_action) {

View File

@ -318,22 +318,12 @@ Application.Filters.filter('toIsoDate', [function () {
};
}]);
Application.Filters.filter('booleanFormat', [ '_t', function (_t) {
return function (boolean) {
if (boolean || (boolean === 'true')) {
return _t('yes');
} else {
return _t('no');
}
};
}]);
Application.Filters.filter('booleanFormat', [ '_t', function (_t) {
return function (boolean) {
if (((typeof boolean === 'boolean') && boolean) || ((typeof boolean === 'string') && (boolean === 'true'))) {
return _t('yes');
return _t('app.shared.buttons.yes');
} else {
return _t('no');
return _t('app.shared.buttons.no');
}
};
}]);
@ -341,7 +331,7 @@ Application.Filters.filter('booleanFormat', [ '_t', function (_t) {
Application.Filters.filter('maxCount', [ '_t', function (_t) {
return function (max) {
if ((typeof max === 'undefined') || (max === null) || ((typeof max === 'number') && (max === 0))) {
return _t('unlimited');
return _t('app.admin.pricing.unlimited');
} else {
return max;
}

View File

@ -36,7 +36,7 @@ angular.module('application.router', ['ui.router'])
resolve: {
logoFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-file' }).$promise; }],
logoBlackFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-black-file' }).$promise; }],
commonTranslations: ['Translations', function (Translations) { return Translations.query(['app.public.common', 'app.shared.buttons', 'app.shared.elements']).$promise; }]
sharedTranslations: ['Translations', function (Translations) { return Translations.query(['app.shared', 'app.public.common']).$promise; }]
},
onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', 'CSRF', function ($rootScope, logoFile, logoBlackFile, CSRF) {
// Retrieve Anti-CSRF tokens from cookies
@ -47,7 +47,10 @@ angular.module('application.router', ['ui.router'])
}]
})
.state('app.public', {
abstract: true
abstract: true,
resolve: {
publicTranslations: ['Translations', function (Translations) { return Translations.query(['app.public']).$promise; }]
}
})
.state('app.logged', {
abstract: true,
@ -55,7 +58,8 @@ angular.module('application.router', ['ui.router'])
authorizedRoles: ['member', 'admin']
},
resolve: {
currentUser: ['Auth', function (Auth) { return Auth.currentUser(); }]
currentUser: ['Auth', function (Auth) { return Auth.currentUser(); }],
loggedTranslations: ['Translations', function (Translations) { return Translations.query(['app.logged']).$promise; }]
},
onEnter: ['$state', '$timeout', 'currentUser', '$rootScope', function ($state, $timeout, currentUser, $rootScope) {
$rootScope.currentUser = currentUser;
@ -67,7 +71,8 @@ angular.module('application.router', ['ui.router'])
authorizedRoles: ['admin']
},
resolve: {
currentUser: ['Auth', function (Auth) { return Auth.currentUser(); }]
currentUser: ['Auth', function (Auth) { return Auth.currentUser(); }],
adminTranslations: ['Translations', function (Translations) { return Translations.query(['app.admin']).$promise; }]
},
onEnter: ['$state', '$timeout', 'currentUser', '$rootScope', function ($state, $timeout, currentUser, $rootScope) {
$rootScope.currentUser = currentUser;
@ -82,9 +87,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "shared/about.html" %>',
controller: 'AboutController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.public.about').$promise; }]
}
})
.state('app.public.home', {
@ -101,7 +103,6 @@ angular.module('application.router', ['ui.router'])
upcomingEventsPromise: ['Event', function (Event) { return Event.upcoming({ limit: 3 }).$promise; }],
homeBlogpostPromise: ['Setting', function (Setting) { return Setting.get({ name: 'home_blogpost' }).$promise; }],
twitterNamePromise: ['Setting', function (Setting) { return Setting.get({ name: 'twitter_name' }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.public.home').$promise; }]
}
})
.state('app.public.privacy', {
@ -111,9 +112,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "shared/privacy.html" %>',
controller: 'PrivacyController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.public.privacy').$promise; }]
}
})
@ -132,7 +130,6 @@ angular.module('application.router', ['ui.router'])
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }],
cguFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'cgu-file' }).$promise; }],
memberPromise: ['Member', 'currentUser', function (Member, currentUser) { return Member.get({ id: currentUser.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.logged.profileCompletion', 'app.shared.user']).$promise; }]
}
})
@ -151,9 +148,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "dashboard/profile.html" %>',
controller: 'DashboardController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.logged.dashboard.profile', 'app.shared.public_profile']).$promise; }]
}
})
.state('app.logged.dashboard.settings', {
@ -167,7 +161,6 @@ angular.module('application.router', ['ui.router'])
resolve: {
groups: ['Group', function (Group) { return Group.query().$promise; }],
activeProviderPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.active().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.logged.dashboard.settings', 'app.shared.user']).$promise; }]
}
})
.state('app.logged.dashboard.projects', {
@ -177,9 +170,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "dashboard/projects.html" %>',
controller: 'DashboardController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.logged.dashboard.projects').$promise; }]
}
})
.state('app.logged.dashboard.trainings', {
@ -189,9 +179,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "dashboard/trainings.html" %>',
controller: 'DashboardController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.logged.dashboard.trainings').$promise; }]
}
})
.state('app.logged.dashboard.events', {
@ -201,9 +188,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "dashboard/events.html" %>',
controller: 'DashboardController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.logged.dashboard.events').$promise; }]
}
})
.state('app.logged.dashboard.invoices', {
@ -213,9 +197,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "dashboard/invoices.html" %>',
controller: 'DashboardController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.logged.dashboard.invoices').$promise; }]
}
})
.state('app.logged.dashboard.wallet', {
@ -228,8 +209,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
walletPromise: ['Wallet', 'currentUser', function (Wallet, currentUser) { return Wallet.getWalletByUser({ user_id: currentUser.id }).$promise; }],
transactionsPromise: ['Wallet', 'walletPromise', function (Wallet, walletPromise) { return Wallet.transactions({ id: walletPromise.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.shared.wallet']).$promise; }]
transactionsPromise: ['Wallet', 'walletPromise', function (Wallet, walletPromise) { return Wallet.transactions({ id: walletPromise.id }).$promise; }]
}
})
@ -243,8 +223,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
memberPromise: ['$stateParams', 'Member', function ($stateParams, Member) { return Member.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.logged.members_show', 'app.shared.public_profile']).$promise; }]
memberPromise: ['$stateParams', 'Member', function ($stateParams, Member) { return Member.get({ id: $stateParams.id }).$promise; }]
}
})
.state('app.logged.members', {
@ -256,8 +235,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
membersPromise: ['Member', function (Member) { return Member.query({ requested_attributes: '[profile]', page: 1, size: 10 }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.logged.members').$promise; }]
membersPromise: ['Member', function (Member) { return Member.query({ requested_attributes: '[profile]', page: 1, size: 10 }).$promise; }]
}
})
@ -273,8 +251,7 @@ angular.module('application.router', ['ui.router'])
resolve: {
themesPromise: ['Theme', function (Theme) { return Theme.query().$promise; }],
componentsPromise: ['Component', function (Component) { return Component.query().$promise; }],
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.public.projects_list').$promise; }]
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }]
}
})
.state('app.logged.projects_new', {
@ -286,8 +263,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
allowedExtensions: ['Project', function (Project) { return Project.allowedExtensions().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.logged.projects_new', 'app.shared.project']).$promise; }]
allowedExtensions: ['Project', function (Project) { return Project.allowedExtensions().$promise; }]
}
})
.state('app.public.projects_show', {
@ -299,8 +275,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
projectPromise: ['$stateParams', 'Project', function ($stateParams, Project) { return Project.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.public.projects_show').$promise; }]
projectPromise: ['$stateParams', 'Project', function ($stateParams, Project) { return Project.get({ id: $stateParams.id }).$promise; }]
}
})
.state('app.logged.projects_edit', {
@ -313,8 +288,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
projectPromise: ['$stateParams', 'Project', function ($stateParams, Project) { return Project.get({ id: $stateParams.id }).$promise; }],
allowedExtensions: ['Project', function (Project) { return Project.allowedExtensions().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.logged.projects_edit', 'app.shared.project']).$promise; }]
allowedExtensions: ['Project', function (Project) { return Project.allowedExtensions().$promise; }]
}
})
@ -328,8 +302,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.machines_list', 'app.shared.training_reservation_modal', 'app.shared.request_training_modal']).$promise; }]
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }]
}
})
.state('app.admin.machines_new', {
@ -339,9 +312,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "machines/new.html" %>',
controller: 'NewMachineController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.machines_new', 'app.shared.machine']).$promise; }]
}
})
.state('app.public.machines_show', {
@ -353,8 +323,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
machinePromise: ['Machine', '$stateParams', function (Machine, $stateParams) { return Machine.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.machines_show', 'app.shared.training_reservation_modal', 'app.shared.request_training_modal']).$promise; }]
machinePromise: ['Machine', '$stateParams', function (Machine, $stateParams) { return Machine.get({ id: $stateParams.id }).$promise; }]
}
})
.state('app.logged.machines_reserve', {
@ -380,11 +349,6 @@ angular.module('application.router', ['ui.router'])
'booking_cancel_delay', \
'subscription_explications_alert']`
}).$promise;
}],
translations: ['Translations', function (Translations) {
return Translations.query(['app.logged.machines_reserve', 'app.shared.plan_subscribe', 'app.shared.member_select',
'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal',
'app.shared.wallet', 'app.shared.coupon_input', 'app.shared.cart']).$promise;
}]
}
})
@ -397,8 +361,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
machinePromise: ['Machine', '$stateParams', function (Machine, $stateParams) { return Machine.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.machines_edit', 'app.shared.machine']).$promise; }]
machinePromise: ['Machine', '$stateParams', function (Machine, $stateParams) { return Machine.get({ id: $stateParams.id }).$promise; }]
}
})
@ -413,8 +376,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.spaces_list']).$promise; }]
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }]
}
})
.state('app.admin.space_new', {
@ -425,9 +387,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "spaces/new.html" %>',
controller: 'NewSpaceController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.space_new', 'app.shared.space']).$promise; }]
}
})
.state('app.public.space_show', {
@ -440,8 +399,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
spacePromise: ['Space', '$stateParams', function (Space, $stateParams) { return Space.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.space_show']).$promise; }]
spacePromise: ['Space', '$stateParams', function (Space, $stateParams) { return Space.get({ id: $stateParams.id }).$promise; }]
}
})
.state('app.admin.space_edit', {
@ -454,8 +412,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
spacePromise: ['Space', '$stateParams', function (Space, $stateParams) { return Space.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.space_edit', 'app.shared.space']).$promise; }]
spacePromise: ['Space', '$stateParams', function (Space, $stateParams) { return Space.get({ id: $stateParams.id }).$promise; }]
}
})
.state('app.logged.space_reserve', {
@ -482,11 +439,6 @@ angular.module('application.router', ['ui.router'])
'booking_cancel_delay', \
'subscription_explications_alert', \
'space_explications_alert']` }).$promise;
}],
translations: ['Translations', function (Translations) {
return Translations.query(['app.logged.space_reserve', 'app.shared.plan_subscribe', 'app.shared.member_select',
'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal',
'app.shared.wallet', 'app.shared.coupon_input', 'app.shared.cart']).$promise;
}]
}
})
@ -501,8 +453,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
trainingsPromise: ['Training', function (Training) { return Training.query({ public_page: true }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.trainings_list']).$promise; }]
trainingsPromise: ['Training', function (Training) { return Training.query({ public_page: true }).$promise; }]
}
})
.state('app.public.training_show', {
@ -514,8 +465,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
trainingPromise: ['Training', '$stateParams', function (Training, $stateParams) { return Training.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.training_show']).$promise; }]
trainingPromise: ['Training', '$stateParams', function (Training, $stateParams) { return Training.get({ id: $stateParams.id }).$promise; }]
}
})
.state('app.logged.trainings_reserve', {
@ -545,11 +495,6 @@ angular.module('application.router', ['ui.router'])
'subscription_explications_alert', \
'training_explications_alert', \
'training_information_message']` }).$promise;
}],
translations: ['Translations', function (Translations) {
return Translations.query(['app.logged.trainings_reserve', 'app.shared.plan_subscribe', 'app.shared.member_select',
'app.shared.stripe', 'app.shared.valid_reservation_modal', 'app.shared.confirm_modify_slot_modal',
'app.shared.wallet', 'app.shared.coupon_input', 'app.shared.cart']).$promise;
}]
}
})
@ -561,9 +506,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "notifications/index.html.erb" %>',
controller: 'NotificationsController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.logged.notifications').$promise; }]
}
})
@ -580,11 +522,7 @@ angular.module('application.router', ['ui.router'])
resolve: {
subscriptionExplicationsPromise: ['Setting', function (Setting) { return Setting.get({ name: 'subscription_explications_alert' }).$promise; }],
plansPromise: ['Plan', function (Plan) { return Plan.query().$promise; }],
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }],
translations: ['Translations', function (Translations) {
return Translations.query(['app.public.plans', 'app.shared.member_select', 'app.shared.stripe', 'app.shared.wallet',
'app.shared.coupon_input']).$promise;
}]
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }]
}
})
@ -600,8 +538,7 @@ angular.module('application.router', ['ui.router'])
resolve: {
categoriesPromise: ['Category', function (Category) { return Category.query().$promise; }],
themesPromise: ['EventTheme', function (EventTheme) { return EventTheme.query().$promise; }],
ageRangesPromise: ['AgeRange', function (AgeRange) { return AgeRange.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.public.events_list').$promise; }]
ageRangesPromise: ['AgeRange', function (AgeRange) { return AgeRange.query().$promise; }]
}
})
.state('app.public.events_show', {
@ -615,11 +552,7 @@ angular.module('application.router', ['ui.router'])
resolve: {
eventPromise: ['Event', '$stateParams', function (Event, $stateParams) { return Event.get({ id: $stateParams.id }).$promise; }],
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }],
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['booking_move_enable', 'booking_move_delay', 'event_explications_alert']" }).$promise; }],
translations: ['Translations', function (Translations) {
return Translations.query(['app.public.events_show', 'app.shared.member_select', 'app.shared.stripe',
'app.shared.valid_reservation_modal', 'app.shared.wallet', 'app.shared.coupon_input']).$promise;
}]
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['booking_move_enable', 'booking_move_delay', 'booking_cancel_enable', 'booking_cancel_delay', 'event_explications_alert']" }).$promise; }]
}
})
@ -638,7 +571,7 @@ angular.module('application.router', ['ui.router'])
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.public.calendar']).$promise; }]
iCalendarPromise: ['ICalendar', function (ICalendar) { return ICalendar.query().$promise; }]
}
})
@ -655,8 +588,19 @@ angular.module('application.router', ['ui.router'])
resolve: {
bookingWindowStart: ['Setting', function (Setting) { return Setting.get({ name: 'booking_window_start' }).$promise; }],
bookingWindowEnd: ['Setting', function (Setting) { return Setting.get({ name: 'booking_window_end' }).$promise; }],
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.calendar').$promise; }]
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }]
}
})
.state('app.admin.calendar.icalendar', {
url: '/admin/calendar/icalendar',
views: {
'main@': {
templateUrl: '<%= asset_path "admin/calendar/icalendar.html" %>',
controller: 'AdminICalendarController'
}
},
resolve: {
iCalendars: ['ICalendar', function (ICalendar) { return ICalendar.query().$promise; }]
}
})
@ -672,8 +616,7 @@ angular.module('application.router', ['ui.router'])
resolve: {
componentsPromise: ['Component', function (Component) { return Component.query().$promise; }],
licencesPromise: ['Licence', function (Licence) { return Licence.query().$promise; }],
themesPromise: ['Theme', function (Theme) { return Theme.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.project_elements').$promise; }]
themesPromise: ['Theme', function (Theme) { return Theme.query().$promise; }]
}
})
.state('app.admin.manage_abuses', {
@ -685,8 +628,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
abusesPromise: ['Abuse', function(Abuse) { return Abuse.query().$promise; }],
translations: ['Translations', function(Translations) { return Translations.query('app.admin.manage_abuses').$promise; }]
abusesPromise: ['Abuse', function(Abuse) { return Abuse.query().$promise; }]
}
})
@ -701,8 +643,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.trainings', 'app.shared.trainings']).$promise; }]
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }]
}
})
.state('app.admin.trainings_new', {
@ -714,8 +655,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.trainings_new', 'app.shared.trainings']).$promise; }]
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }]
}
})
.state('app.admin.trainings_edit', {
@ -728,8 +668,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
trainingPromise: ['Training', '$stateParams', function (Training, $stateParams) { return Training.get({ id: $stateParams.id }).$promise; }],
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.shared.trainings').$promise; }]
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }]
}
})
// events
@ -746,8 +685,7 @@ angular.module('application.router', ['ui.router'])
categoriesPromise: ['Category', function (Category) { return Category.query().$promise; }],
themesPromise: ['EventTheme', function (EventTheme) { return EventTheme.query().$promise; }],
ageRangesPromise: ['AgeRange', function (AgeRange) { return AgeRange.query().$promise; }],
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.events').$promise; }]
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }]
}
})
.state('app.admin.events_new', {
@ -762,8 +700,7 @@ angular.module('application.router', ['ui.router'])
categoriesPromise: ['Category', function (Category) { return Category.query().$promise; }],
themesPromise: ['EventTheme', function (EventTheme) { return EventTheme.query().$promise; }],
ageRangesPromise: ['AgeRange', function (AgeRange) { return AgeRange.query().$promise; }],
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.events_new', 'app.shared.event']).$promise; }]
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }]
}
})
.state('app.admin.events_edit', {
@ -779,8 +716,7 @@ angular.module('application.router', ['ui.router'])
categoriesPromise: ['Category', function (Category) { return Category.query().$promise; }],
themesPromise: ['EventTheme', function (EventTheme) { return EventTheme.query().$promise; }],
ageRangesPromise: ['AgeRange', function (AgeRange) { return AgeRange.query().$promise; }],
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.events_edit', 'app.shared.event']).$promise; }]
priceCategoriesPromise: ['PriceCategory', function (PriceCategory) { return PriceCategory.query().$promise; }]
}
})
.state('app.admin.event_reservations', {
@ -793,8 +729,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
eventPromise: ['Event', '$stateParams', function (Event, $stateParams) { return Event.get({ id: $stateParams.id }).$promise; }],
reservationsPromise: ['Reservation', '$stateParams', function (Reservation, $stateParams) { return Reservation.query({ reservable_id: $stateParams.id, reservable_type: 'Event' }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.event_reservations').$promise; }]
reservationsPromise: ['Reservation', '$stateParams', function (Reservation, $stateParams) { return Reservation.query({ reservable_id: $stateParams.id, reservable_type: 'Event' }).$promise; }]
}
})
@ -812,7 +747,6 @@ angular.module('application.router', ['ui.router'])
groups: ['Group', function (Group) { return Group.query().$promise; }],
machinesPricesPromise: ['Price', function (Price) { return Price.query({ priceable_type: 'Machine', plan_id: 'null' }).$promise; }],
trainingsPricingsPromise: ['TrainingsPricing', function (TrainingsPricing) { return TrainingsPricing.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.pricing', 'app.shared.member_select', 'app.shared.coupon']).$promise; }],
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
machineCreditsPromise: ['Credit', function (Credit) { return Credit.query({ creditable_type: 'Machine' }).$promise; }],
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
@ -840,9 +774,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "admin/plans/new.html" %>',
controller: 'NewPlanController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.plans.new', 'app.shared.plan']).$promise; }]
}
})
.state('app.admin.plans.edit', {
@ -857,8 +788,7 @@ angular.module('application.router', ['ui.router'])
spaces: ['Space', function (Space) { return Space.query().$promise; }],
machines: ['Machine', function (Machine) { return Machine.query().$promise; }],
plans: ['Plan', function (Plan) { return Plan.query().$promise; }],
planPromise: ['Plan', '$stateParams', function (Plan, $stateParams) { return Plan.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.plans.edit', 'app.shared.plan']).$promise; }]
planPromise: ['Plan', '$stateParams', function (Plan, $stateParams) { return Plan.get({ id: $stateParams.id }).$promise; }]
}
})
@ -870,9 +800,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "admin/coupons/new.html" %>',
controller: 'NewCouponController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.coupons_new', 'app.shared.coupon']).$promise; }]
}
})
.state('app.admin.coupons_edit', {
@ -884,8 +811,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
couponPromise: ['Coupon', '$stateParams', function (Coupon, $stateParams) { return Coupon.get({ id: $stateParams.id }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.coupons_edit', 'app.shared.coupon']).$promise; }]
couponPromise: ['Coupon', '$stateParams', function (Coupon, $stateParams) { return Coupon.get({ id: $stateParams.id }).$promise; }]
}
})
@ -914,8 +840,7 @@ angular.module('application.router', ['ui.router'])
query: { number: '', customer: '', date: null, order_by: '-reference', page: 1, size: 20 }
}).$promise;
}],
closedPeriods: [ 'AccountingPeriod', function(AccountingPeriod) { return AccountingPeriod.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.invoices').$promise; }]
closedPeriods: [ 'AccountingPeriod', function(AccountingPeriod) { return AccountingPeriod.query().$promise; }]
}
})
@ -945,8 +870,7 @@ angular.module('application.router', ['ui.router'])
adminsPromise: ['Admin', function (Admin) { return Admin.query().$promise; }],
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }],
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }],
authProvidersPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.members').$promise; }]
authProvidersPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.query().$promise; }]
}
})
.state('app.admin.members_new', {
@ -956,9 +880,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "admin/members/new.html" %>',
controller: 'NewMemberController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.members_new', 'app.shared.user', 'app.shared.user_admin']).$promise; }]
}
})
.state('app.admin.members_import', {
@ -970,7 +891,6 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.members_import', 'app.shared.user', 'app.shared.user_admin']).$promise; }],
tags: ['Tag', function(Tag) { return Tag.query().$promise }]
}
})
@ -983,7 +903,6 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.members_import_result', 'app.shared.user', 'app.shared.user_admin']).$promise; }],
importItem: ['Import', '$stateParams', function(Import, $stateParams) { return Import.get({ id: $stateParams.id }).$promise }]
}
})
@ -1000,8 +919,7 @@ angular.module('application.router', ['ui.router'])
activeProviderPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.active().$promise; }],
walletPromise: ['Wallet', '$stateParams', function (Wallet, $stateParams) { return Wallet.getWalletByUser({ user_id: $stateParams.id }).$promise; }],
transactionsPromise: ['Wallet', 'walletPromise', function (Wallet, walletPromise) { return Wallet.transactions({ id: walletPromise.id }).$promise; }],
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.members_edit', 'app.shared.user', 'app.shared.user_admin', 'app.shared.wallet']).$promise; }]
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }]
}
})
.state('app.admin.admins_new', {
@ -1011,9 +929,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "admin/admins/new.html" %>',
controller: 'NewAdminController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.admin.admins_new').$promise; }]
}
})
@ -1028,8 +943,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
mappingFieldsPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.mapping_fields().$promise; }],
authProvidersPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.authentication_new', 'app.shared.authentication', 'app.shared.oauth2']).$promise; }]
authProvidersPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.query().$promise; }]
}
})
.state('app.admin.authentication_edit', {
@ -1042,8 +956,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
providerPromise: ['AuthProvider', '$stateParams', function (AuthProvider, $stateParams) { return AuthProvider.get({ id: $stateParams.id }).$promise; }],
mappingFieldsPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.mapping_fields().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.authentication_edit', 'app.shared.authentication', 'app.shared.oauth2']).$promise; }]
mappingFieldsPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.mapping_fields().$promise; }]
}
})
@ -1058,8 +971,7 @@ angular.module('application.router', ['ui.router'])
},
resolve: {
membersPromise: ['Member', function (Member) { return Member.mapping().$promise; }],
statisticsPromise: ['Statistics', function (Statistics) { return Statistics.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.statistics').$promise; }]
statisticsPromise: ['Statistics', function (Statistics) { return Statistics.query().$promise; }]
}
})
.state('app.admin.stats_graphs', {
@ -1069,9 +981,6 @@ angular.module('application.router', ['ui.router'])
templateUrl: '<%= asset_path "admin/statistics/graphs.html" %>',
controller: 'GraphsController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query('app.admin.stats_graphs').$promise; }]
}
})
@ -1121,8 +1030,7 @@ angular.module('application.router', ['ui.router'])
cguFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'cgu-file' }).$promise; }],
cgvFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'cgv-file' }).$promise; }],
faviconFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'favicon-file' }).$promise; }],
profileImageFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'profile-image-file' }).$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.settings').$promise; }]
profileImageFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'profile-image-file' }).$promise; }]
}
})
@ -1136,8 +1044,7 @@ angular.module('application.router', ['ui.router'])
}
},
resolve: {
clientsPromise: ['OpenAPIClient', function (OpenAPIClient) { return OpenAPIClient.query().$promise; }],
translations: ['Translations', function (Translations) { return Translations.query('app.admin.open_api_clients').$promise; }]
clientsPromise: ['OpenAPIClient', function (OpenAPIClient) { return OpenAPIClient.query().$promise; }]
}
});
}

View File

@ -0,0 +1,5 @@
'use strict';
Application.Services.factory('Ical', ['$resource', function ($resource) {
return $resource('/api/ical/externals');
}]);

View File

@ -0,0 +1,17 @@
'use strict';
Application.Services.factory('ICalendar', ['$resource', function ($resource) {
return $resource('/api/i_calendar/:id',
{ id: '@id' }, {
events: {
method: 'GET',
url: '/api/i_calendar/:id/events'
},
sync: {
method: 'POST',
url: '/api/i_calendar/:id/sync',
params: { id: '@id' }
}
}
);
}]);

View File

@ -12,6 +12,10 @@
.bg-stage { background-color: $violet; }
.bg-success { background-color: $brand-success; }
.bg-info { background-color: $brand-info; }
.border-machine { border-color: $beige !important; }
.border-space { border-color: $cyan !important; }
.border-formation { border-color: $violet !important; }
.border-event { border-color: $japonica !important; }
.bg-black-light { background-color: #424242 !important; }

View File

@ -290,6 +290,11 @@
@include border-radius(3px);
padding: 5px 10px;
}
&.well-disabled {
border-color: $gray-lighter;
background-color: $gray-lighter;
color: $gray-light;
}
}
.read {
@ -629,3 +634,42 @@ padding: 10px;
.help-block.error {
color: #ff565d;
}
.disabled {
background-color: $gray-lighter;
color: $gray-light;
& a {
color: $gray;
}
.canceled-marker {
float: right;
top: -13px;
position: relative;
color: red;
text-transform: uppercase;
}
}
.calendar-legend-block {
text-align: right;
padding-right: 2em;
h4 {
font-size: 12px;
font-style: italic;
}
.legends {
display: flex;
flex-direction: row-reverse;
}
.calendar-legend {
border: 1px solid;
border-left: 3px solid;
border-radius: 3px;
font-size: 10px;
padding: 2px;
margin-left: 10px;
display: inline-block;
}
}

View File

@ -101,7 +101,6 @@
}
}
body.container{
padding: 0;
}

View File

@ -126,6 +126,10 @@
}
}
.fc-selected {
box-shadow: 0 6px 10px 0 rgba(0,0,0,0.14),0 1px 18px 0 rgba(0,0,0,0.12),0 3px 5px -1px rgba(0,0,0,0.2);
}

View File

@ -102,6 +102,7 @@ p, .widget p {
.text-italic { font-style: italic; }
.text-left { text-align: left !important; }
.text-center { text-align: center; }
.text-right { text-align: right; }
@ -379,6 +380,10 @@ p, .widget p {
justify-content: center;
}
.pointer {
cursor: pointer;
}
@media screen and (min-width: $screen-lg-min) {
.b-r-lg {border-right: 1px solid $border-color; }
.hide-b-r-lg { border: none !important; }

View File

@ -32,11 +32,7 @@
@import "app.buttons";
@import "app.components";
@import "app.plugins";
@import "modules/invoice";
@import "modules/signup";
@import "modules/abuses";
@import "modules/cookies";
@import "modules/stripe";
@import "modules/*";
@import "app.responsive";

View File

@ -0,0 +1,24 @@
.calendar-form {
margin : 2em;
border: 1px solid #ddd;
border-radius: 3px;
padding: 1em;
& > .input-group, & > .minicolors {
margin-top: 1em;
}
}
.calendar-name {
font-weight: 600;
font-style: italic;
}
.calendar-url {
overflow: hidden;
}
.external-calendar-legend {
border-left: 3px solid;
border-radius: 3px;
}

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'manage_abuses.abuses_list' }}</h1>
<h1 translate>{{ 'app.admin.manage_abuses.abuses_list' }}</h1>
</section>
</div>
</div>
@ -15,24 +15,24 @@
<section class="m-lg">
<div class="row m-b-md">
<span ng-show="abuses.length === 0" translate>{{ 'manage_abuses.no_reports' }}</span>
<span ng-show="abuses.length === 0" translate>{{ 'app.admin.manage_abuses.no_reports' }}</span>
<ul ng-show="abuses.length > 0">
<li class="abuse" ng-repeat="abuse in abuses">
<div class="signaled">
<a ui-sref="app.public.projects_show({id:abuse.signaled.slug})">{{abuse.signaled.name}}</a>,
<span translate>{{ 'manage_abuses.published_by' }}</span>
<span translate>{{ 'app.admin.manage_abuses.published_by' }}</span>
<a ui-sref="app.admin.members_edit({id:abuse.signaled.author.id})">{{abuse.signaled.author.full_name}}</a>,
<span translate>{{ 'manage_abuses.at_date' }}</span>
<span translate>{{ 'app.admin.manage_abuses.at_date' }}</span>
<span>{{abuse.signaled.published_at | amDateFormat:'L' }}</span>
<button class="btn btn-success" ng-click="confirmProcess(abuse.id)">
<i class="fa fa-check"></i>
</button>
</div>
<div class="report">
<span translate>{{ 'manage_abuses.at_date' }}</span>
<span translate>{{ 'app.admin.manage_abuses.at_date' }}</span>
<span>{{abuse.created_at | amDateFormat:'L' }}</span>,
<a href="mailto:{{abuse.email}}">{{abuse.first_name}} {{abuse.last_name}}</a>
<span translate>{{ 'manage_abuses.has_reported' }}</span>
<span translate>{{ 'app.admin.manage_abuses.has_reported' }}</span>
<cite>{{ abuse.message }}</cite>
</div>
</li>

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'add_an_administrator' }}</h1>
<h1 translate>{{ 'app.admin.admins_new.add_an_administrator' }}</h1>
</section>
</div>
@ -20,12 +20,12 @@
<form role="form" name="adminForm" class="form-horizontal" novalidate>
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<ng-include src="'<%= asset_path 'shared/_admin_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "shared/_admin_form.html" %>'"></ng-include>
</div>
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'save' | translate}}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-click="saveAdmin()" ng-disabled="adminForm.$invalid"/>
<input type="submit" value="{{ 'app.shared.buttons.save' | translate}}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-click="saveAdmin()" ng-disabled="adminForm.$invalid"/>
</div>
</section>
</form>

View File

@ -1,16 +1,16 @@
<div class="modal-header">
<h3 class="modal-title"><span translate>{{ 'data_mapping' }}</span> : {{field.local_field}}</h3>
<h3 class="modal-title"><span translate>{{ 'app.shared.authentication.data_mapping' }}</span> : {{field.local_field}}</h3>
</div>
<div class="modal-body m-lg">
<div>
<span translate>{{ 'expected_data_type' }}</span> : {{datatype}}
<span translate>{{ 'app.shared.authentication.expected_data_type' }}</span> : {{datatype}}
</div>
<form name="mappingForm" class="m-t-md">
<ng-switch on="datatype">
<!-- BOOLEAN -->
<div ng-switch-when="boolean">
<label for="add_mapping" translate>{{ 'mappings' }}</label>
<label for="add_mapping" translate>{{ 'app.shared.authentication.mappings' }}</label>
<ul class="list-unstyled">
<li class="m-t-sm m-l">
<input type="text"
@ -35,7 +35,7 @@
<!-- DATE -->
<div ng-switch-when="date">
<label for="date_format" translate>{{ 'input_format' }}</label>
<label for="date_format" translate>{{ 'app.shared.authentication.input_format' }}</label>
<select name="date_format"
id="date_format"
class="form-control"
@ -46,7 +46,7 @@
<!-- INTEGER -->
<div ng-switch-when="integer">
<label for="add_mapping" translate>{{ 'mappings' }}</label>
<label for="add_mapping" translate>{{ 'app.shared.authentication.mappings' }}</label>
<button class="btn btn-default pull-right" ng-click="addIntegerMapping()"><i class="fa fa-plus"></i></button>
<ul class="list-unstyled">
<li ng-repeat="map in transformation.rules.mapping" class="m-t-sm m-l">
@ -60,6 +60,6 @@
</form>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()" ng-disabled="!mappingForm.$valid" ng-if="datatype != 'string' && datatype != 'text'" translate>{{ 'confirm' }}</button>
<button class="btn btn-warning" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<button class="btn btn-primary" ng-click="ok()" ng-disabled="!mappingForm.$valid" ng-if="datatype != 'string' && datatype != 'text'" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-warning" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -1,5 +1,5 @@
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[name]'].$dirty && providerForm['auth_provider[name]'].$invalid}">
<label for="provider_name" class="col-sm-3 control-label" translate>{{ 'name' }}</label>
<label for="provider_name" class="col-sm-3 control-label" translate>{{ 'app.shared.authentication.name' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.name"
@ -8,23 +8,23 @@
id="provider_name"
ng-disabled="mode == 'edition'"
required />
<span class="help-block" ng-show="providerForm['auth_provider[name]'].$dirty && providerForm['auth_provider[name]'].$error.required" translate>{{ 'provider_name_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[name]'].$dirty && providerForm['auth_provider[name]'].$error.required" translate>{{ 'app.shared.authentication.provider_name_is_required' }}</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[providable_type]'].$dirty && providerForm['auth_provider[providable_type]'].$invalid}">
<label for="provider_type" class="col-sm-3 control-label" translate>{{ 'authentication_type' }}</label>
<label for="provider_type" class="col-sm-3 control-label" translate>{{ 'app.shared.authentication.authentication_type' }}</label>
<div class="col-sm-9">
<select ng-model="provider.providable_type"
ng-change="updateProvidable()"
class="form-control"
name="auth_provider[providable_type]"
id="provider_type"
ng-options="key as (value | translate) for (key, value) in authMethods"
ng-options="key as methodName(key) for (key, value) in authMethods"
ng-disabled="mode == 'edition'"
required>
</select>
<input type="hidden" name="auth_provider[type]" ng-value="provider.type" />
<span class="help-block" ng-show="providerForm['auth_provider[providable_type]'].$dirty && providerForm['auth_provider[providable_type]'].$error.required" translate>{{ 'authentication_type_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[providable_type]'].$dirty && providerForm['auth_provider[providable_type]'].$error.required" translate>{{ 'app.shared.authentication.authentication_type_is_required' }}</span>
</div>
</div>

View File

@ -1,7 +1,7 @@
<hr/>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[base_url]'].$dirty && providerForm['auth_provider[base_url]'].$invalid}">
<label for="provider_base_url" class="col-sm-3 control-label" translate>{{ 'common_url' }}</label>
<label for="provider_base_url" class="col-sm-3 control-label" translate>{{ 'app.shared.oauth2.common_url' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.providable_attributes.base_url"
@ -11,13 +11,13 @@
placeholder="https://sso.example.net..."
required
url>
<span class="help-block" ng-show="providerForm['auth_provider[base_url]'].$dirty && providerForm['auth_provider[base_url]'].$error.required" translate>{{ 'common_url_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[base_url]'].$error.url" translate>{{ 'provided_url_is_not_a_valid_url' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[base_url]'].$dirty && providerForm['auth_provider[base_url]'].$error.required" translate>{{ 'app.shared.oauth2.common_url_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[base_url]'].$error.url" translate>{{ 'app.shared.oauth2.provided_url_is_not_a_valid_url' }}</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[authorization_endpoint]'].$dirty && providerForm['auth_provider[authorization_endpoint]'].$invalid}">
<label for="provider_authorization_endpoint" class="col-sm-3 control-label" translate>{{ 'authorization_endpoint' }}</label>
<label for="provider_authorization_endpoint" class="col-sm-3 control-label" translate>{{ 'app.shared.oauth2.authorization_endpoint' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.providable_attributes.authorization_endpoint"
@ -27,13 +27,13 @@
placeholder="/oauth2/auth..."
required
endpoint>
<span class="help-block" ng-show="providerForm['auth_provider[authorization_endpoint]'].$dirty && providerForm['auth_provider[authorization_url]'].$error.required" translate>{{ 'oauth2_authorization_endpoint_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[authorization_endpoint]'].$error.endpoint" translate>{{ 'provided_endpoint_is_not_valid' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[authorization_endpoint]'].$dirty && providerForm['auth_provider[authorization_url]'].$error.required" translate>{{ 'app.shared.oauth2.oauth2_authorization_endpoint_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[authorization_endpoint]'].$error.endpoint" translate>{{ 'app.shared.oauth2.provided_endpoint_is_not_valid' }}</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[token_endpoint]'].$dirty && providerForm['auth_provider[token_endpoint]'].$invalid}">
<label for="provider_token_endpoint" class="col-sm-3 control-label" translate>{{ 'token_acquisition_endpoint' }}</label>
<label for="provider_token_endpoint" class="col-sm-3 control-label" translate>{{ 'app.shared.oauth2.token_acquisition_endpoint' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.providable_attributes.token_endpoint"
@ -43,13 +43,13 @@
placeholder="/oauth2/token..."
required
endpoint>
<span class="help-block" ng-show="providerForm['auth_provider[token_endpoint]'].$dirty && providerForm['auth_provider[token_endpoint]'].$error.required" translate>{{ 'oauth2_token_acquisition_endpoint_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[token_endpoint]'].$error.endpoint" translate>{{ 'provided_endpoint_is_not_valid' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[token_endpoint]'].$dirty && providerForm['auth_provider[token_endpoint]'].$error.required" translate>{{ 'app.shared.oauth2.oauth2_token_acquisition_endpoint_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[token_endpoint]'].$error.endpoint" translate>{{ 'app.shared.oauth2.provided_endpoint_is_not_valid' }}</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[profile_url]'].$dirty && providerForm['auth_provider[profile_url]'].$invalid}">
<label for="provider_profile_url" class="col-sm-3 control-label" translate>{{ 'profil_edition_url' }}</label>
<label for="provider_profile_url" class="col-sm-3 control-label" translate>{{ 'app.shared.oauth2.profil_edition_url' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.providable_attributes.profile_url"
@ -59,13 +59,13 @@
placeholder="https://exemple.net/user..."
required
url>
<span class="help-block" ng-show="providerForm['auth_provider[profile_url]'].$dirty && providerForm['auth_provider[profile_url]'].$error.required" translate>{{ 'profile_edition_url_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[profile_url]'].$error.url" translate>{{ 'provided_url_is_not_a_valid_url' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[profile_url]'].$dirty && providerForm['auth_provider[profile_url]'].$error.required" translate>{{ 'app.shared.oauth2.profile_edition_url_is_required' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[profile_url]'].$error.url" translate>{{ 'app.shared.oauth2.provided_url_is_not_a_valid_url' }}</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[client_id]'].$dirty && providerForm['auth_provider[client_id]'].$invalid}">
<label for="provider_client_id" class="col-sm-3 control-label" translate>{{ 'client_identifier' }}</label>
<label for="provider_client_id" class="col-sm-3 control-label" translate>{{ 'app.shared.oauth2.client_identifier' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.providable_attributes.client_id"
@ -73,12 +73,12 @@
name="auth_provider[client_id]"
id="provider_client_id"
required>
<span class="help-block" ng-show="providerForm['auth_provider[client_id]'].$dirty && providerForm['auth_provider[client_id]'].$error.required" translate>{{ 'oauth2_client_identifier_is_required' }} {{ 'obtain_it_when_registering_with_your_provider' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[client_id]'].$dirty && providerForm['auth_provider[client_id]'].$error.required" translate>{{ 'app.shared.oauth2.oauth2_client_identifier_is_required' }} {{ 'obtain_it_when_registering_with_your_provider' }}</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[client_secret]'].$dirty && providerForm['auth_provider[client_secret]'].$invalid}">
<label for="provider_client_secret" class="col-sm-3 control-label" translate>{{ 'client_secret' }}</label>
<label for="provider_client_secret" class="col-sm-3 control-label" translate>{{ 'app.shared.oauth2.client_secret' }}</label>
<div class="col-sm-9">
<input type="text"
ng-model="provider.providable_attributes.client_secret"
@ -86,8 +86,8 @@
name="auth_provider[client_secret]"
id="provider_client_secret"
required>
<span class="help-block" ng-show="providerForm['auth_provider[client_secret]'].$dirty && providerForm['auth_provider[client_secret]'].$error.required" translate>{{ 'oauth2_client_secret_is_required' }} {{ 'obtain_it_when_registering_with_your_provider' }}</span>
<span class="help-block" ng-show="providerForm['auth_provider[client_secret]'].$dirty && providerForm['auth_provider[client_secret]'].$error.required" translate>{{ 'app.shared.oauth2.oauth2_client_secret_is_required' }} {{ 'obtain_it_when_registering_with_your_provider' }}</span>
</div>
</div>
<ng-include src="'<%= asset_path 'admin/authentications/_oauth2_mapping.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/authentications/_oauth2_mapping.html" %>'"></ng-include>

View File

@ -1,15 +1,15 @@
<h4 class="m-l m-t-xl" translate>{{ 'define_the_fields_mapping' }}</h4>
<h4 class="m-l m-t-xl" translate>{{ 'app.shared.oauth2.define_the_fields_mapping' }}</h4>
<button type="button" class="btn btn-success m-l m-b" ng-click="newMapping = {}"><i class="fa fa-plus"></i> {{ 'add_a_match' | translate }}</button>
<button type="button" class="btn btn-success m-l m-b" ng-click="newMapping = {}"><i class="fa fa-plus"></i> {{ 'app.shared.oauth2.add_a_match' | translate }}</button>
<table class="table">
<thead>
<tr>
<th translate>{{ 'model' }}</th>
<th translate>{{ 'field' }}</th>
<th translate>{{ 'api_endpoint_url' }}</th>
<th translate>{{ 'api_type' }}</th>
<th translate>{{ 'api_fields' }}</th>
<th translate>{{ 'app.shared.oauth2.model' }}</th>
<th translate>{{ 'app.shared.oauth2.field' }}</th>
<th translate>{{ 'app.shared.oauth2.api_endpoint_url' }}</th>
<th translate>{{ 'app.shared.oauth2.api_type' }}</th>
<th translate>{{ 'app.shared.oauth2.api_fields' }}</th>
<th style="width: 6.4em;"></th>
</tr>
</thead>

View File

@ -9,7 +9,7 @@
</div>
<div class="col-md-8 b-l b-r">
<section class="heading-title">
<h1>{{ 'provider' | translate }} {{provider.name}}</h1>
<h1>{{ 'app.admin.authentication_edit.provider' | translate }} {{provider.name}}</h1>
</section>
</div>
@ -17,7 +17,7 @@
<div class="col-md-3">
<section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }}
{{ 'app.shared.buttons.cancel' }}
</div>
</section>
@ -35,13 +35,13 @@
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<ng-include src="'<%= asset_path 'admin/authentications/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path 'admin/authentications/_oauth2.html'%>'" ng-if="provider.providable_type == 'OAuth2Provider'"></ng-include>
<ng-include src="'<%= asset_path "admin/authentications/_form.html" %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/authentications/_oauth2.html"%>'" ng-if="provider.providable_type == 'OAuth2Provider'"></ng-include>
</div> <!-- ./panel-body -->
<div class="panel-footer no-padder">
<input type="button" value="{{ 'confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="providerForm.$invalid" ng-click="updateProvider()"/>
<input type="button" value="{{ 'app.shared.buttons.confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="providerForm.$invalid" ng-click="updateProvider()"/>
</div>
</section>
</form>

View File

@ -2,23 +2,23 @@
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'search_for_an_authentication_provider' | translate }}">
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'app.admin.members.authentication_form.search_for_an_authentication_provider' | translate }}">
</div>
</div>
</div>
<div class="col-md-12">
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.authentication_new" translate>{{ 'add_a_new_authentication_provider' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.authentication_new" translate>{{ 'app.admin.members.authentication_form.add_a_new_authentication_provider' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:15%" translate>{{ 'name' }}</th>
<th style="width:15%" translate>{{ 'app.admin.members.authentication_form.name' }}</th>
<th style="width:15%" translate>{{ 'strategy_name' }}</th>
<th style="width:15%" translate>{{ 'app.admin.members.authentication_form.strategy_name' }}</th>
<th style="width:15%" translate>{{ 'type' }}</th>
<th style="width:15%" translate>{{ 'app.admin.members.authentication_form.type' }}</th>
<th style="width:15%" translate>{{ 'state' }}</th>
<th style="width:15%" translate>{{ 'app.admin.members.authentication_form.state' }}</th>
<th style="width:10%"></th>
</tr>
@ -31,7 +31,7 @@
<td>{{ getState(provider.status) }}</td>
<td>
<button class="btn btn-default" ui-sref="app.admin.authentication_edit({id:provider.id})">
<i class="fa fa-pencil-square-o"></i> {{ 'edit' | translate }}
<i class="fa fa-pencil-square-o"></i> {{ 'app.shared.buttons.edit' | translate }}
</button>
<button class="btn btn-danger" ng-if="provider.status != 'active'" ng-click="destroyProvider(providers, provider)">
<i class="fa fa-trash-o"></i>

View File

@ -9,7 +9,7 @@
</div>
<div class="col-md-8 b-l b-r">
<section class="heading-title">
<h1 translate>{{ 'add_a_new_authentication_provider' }}</h1>
<h1 translate>{{ 'app.admin.authentication_new.add_a_new_authentication_provider' }}</h1>
</section>
</div>
@ -17,7 +17,7 @@
<div class="col-md-3">
<section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }}
{{ 'app.shared.buttons.cancel' }}
</div>
</section>
@ -36,13 +36,13 @@
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<ng-include src="'<%= asset_path 'admin/authentications/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path 'admin/authentications/_oauth2.html'%>'" ng-if="provider.providable_type == 'OAuth2Provider'"></ng-include>
<ng-include src="'<%= asset_path "admin/authentications/_form.html" %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/authentications/_oauth2.html" %>'" ng-if="provider.providable_type == 'OAuth2Provider'"></ng-include>
</div> <!-- ./panel-body -->
<div class="panel-footer no-padder">
<input type="button" value="{{ 'save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="providerForm.$invalid" ng-click="registerProvider()"/>
<input type="button" value="{{ 'app.shared.buttons.save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="providerForm.$invalid" ng-click="registerProvider()"/>
</div>
</section>
</form>

View File

@ -5,17 +5,17 @@
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
</section>
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
<div class="col-xs-10 b-l b-r-md">
<section class="heading-title">
<h1 translate>{{ 'admin_calendar.calendar_management' }}</h1>
<h1 translate>{{ 'app.admin.calendar.calendar_management' }}</h1>
</section>
</div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
<section class="heading-actions wrapper" ng-class="{'p-s': !fablabWithoutSpaces}">
<span class="badge text-sm bg-formation" ng-class="{'m-t-sm': fablabWithoutSpaces}" translate>{{ 'admin_calendar.trainings' }}</span><br>
<span class="badge text-sm bg-machine" translate>{{ 'admin_calendar.machines' }}</span><br>
<span class="badge text-sm bg-space" ng-hide="fablabWithoutSpaces" translate>{{ 'admin_calendar.spaces' }}</span>
<div class="col-xs-1">
<section class="heading-actions wrapper">
<a role="button" ui-sref="app.admin.calendar.icalendar" class="btn btn-default b-2x rounded pointer m-t-sm">
<i class="fa fa-exchange" aria-hidden="true"></i>
</a>
</section>
</div>
@ -27,6 +27,15 @@
<div class="col-sm-12 col-md-12 col-lg-9">
<div ui-calendar="calendarConfig" ng-model="eventSources" calendar="calendar" class="wrapper-lg"></div>
<div class="calendar-legend-block">
<h4 translate>{{ 'app.admin.calendar.legend' }}</h4>
<div class="legends">
<span class="calendar-legend text-sm border-formation" translate>{{ 'app.admin.calendar.trainings' }}</span><br>
<span class="calendar-legend text-sm border-machine" translate>{{ 'app.admin.calendar.machines' }}</span><br>
<span class="calendar-legend text-sm border-space" ng-hide="fablabWithoutSpaces" translate>{{ 'app.admin.calendar.spaces' }}</span>
<span class="calendar-legend text-sm border-event" ng-show="eventsInCalendar" translate>{{ 'app.admin.calendar.events' }}</span>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-3">
@ -35,17 +44,17 @@
ng-href="api/availabilities/export_index.xlsx"
target="export-frame"
ng-click="alertExport('index')"
uib-popover="{{ 'admin_calendar.availabilities_notice' | translate}}"
uib-popover="{{ 'app.admin.calendar.availabilities_notice' | translate}}"
popover-trigger="mouseenter"
popover-placement="bottom">
<i class="fa fa-file-excel-o"></i> {{ 'admin_calendar.availabilities' | translate }}
<i class="fa fa-file-excel-o"></i> {{ 'app.admin.calendar.availabilities' | translate }}
</a>
<iframe name="export-frame" height="0" width="0" class="none"></iframe>
</div>
<div class="widget panel b-a m m-t-lg" ng-if="availability">
<div class="widget panel b-a m m-t-lg" ng-if="availability" ng-hide="availability.available_type == 'event'">
<div class="panel-heading b-b small">
<h3 translate>{{ 'admin_calendar.ongoing_reservations' }}</h3>
<h3 translate>{{ 'app.admin.calendar.ongoing_reservations' }}</h3>
</div>
<div class="widget-content no-bg auto wrapper" ng-class="{'reservations-locked': availability.lock}">
<ul class="list-unstyled" ng-if="reservations.length > 0">
@ -56,14 +65,14 @@
<span class="btn btn-warning btn-xs" ng-click="cancelBooking(r)" ng-if="!r.canceled_at"><i class="fa fa-times red"></i></span>
</li>
</ul>
<div ng-show="reservations.length == 0" translate>{{ 'admin_calendar.no_reservations' }}</div>
<div class="m-t" ng-show="availability.lock"><i class="fa fa-ban"/> <span class="m-l-xs" translate>{{ 'admin_calendar.reservations_locked' }}</span></div>
<div ng-show="reservations.length == 0" translate>{{ 'app.admin.calendar.no_reservations' }}</div>
<div class="m-t" ng-show="availability.lock"><i class="fa fa-ban"></i> <span class="m-l-xs" translate>{{ 'app.admin.calendar.reservations_locked' }}</span></div>
</div>
</div>
<div class="widget panel b-a m m-t-lg" ng-if="availability.machine_ids.length > 0">
<div class="panel-heading b-b small">
<h3 translate>{{ 'admin_calendar.machines' }}</h3>
<h3 translate>{{ 'app.admin.calendar.machines' }}</h3>
</div>
<div class="widget-content no-bg auto wrapper">
<ul class="list-unstyled">
@ -77,26 +86,40 @@
<div class="widget panel b-a m m-t-lg" ng-if="availability" >
<div class="panel-heading b-b small">
<h3 translate>{{ 'admin_calendar.actions' }}</h3>
<h3 translate>{{ 'app.admin.calendar.actions' }}</h3>
</div>
<div class="widget-content no-bg auto wrapper">
<div class="widget-content no-bg auto wrapper" ng-hide="availability.available_type == 'event'">
<button class="btn btn-default" ng-click="toggleLockReservations()">
<span ng-hide="availability.lock">
<i class="fa fa-stop" />
<span class="m-l-xs" translate>{{ 'admin_calendar.block_reservations' }}</span>
<i class="fa fa-stop"></i>
<span class="m-l-xs" translate>{{ 'app.admin.calendar.block_reservations' }}</span>
</span>
<span ng-show="availability.lock">
<i class="fa fa-play" />
<span class="m-l-xs" translate>{{ 'admin_calendar.allow_reservations' }}</span>
<i class="fa fa-play"></i>
<span class="m-l-xs" translate>{{ 'app.admin.calendar.allow_reservations' }}</span>
</span>
</button>
<button class="btn btn-default m-t" ng-click="removeSlot()">
<span>
<i class="fa fa-trash" />
<span class="m-l-xs" translate>{{ 'admin_calendar.delete_slot' }}</span>
<i class="fa fa-trash"></i>
<span class="m-l-xs" translate>{{ 'app.admin.calendar.delete_slot' }}</span>
</span>
</button>
</div>
<div class="widget-content no-bg auto wrapper" ng-show="availability.available_type == 'event'">
<a class="btn btn-default pointer" ui-sref="app.admin.events_edit({id: availability.event_id})">
<span>
<i class="fa fa-edit"></i>
<span class="m-l-xs" translate>{{ 'app.admin.calendar.edit_event' }}</span>
</span>
</a>
<a class="btn btn-default m-t pointer" ui-sref="app.admin.event_reservations({id: availability.event_id})">
<span>
<i class="fa fa-bookmark"></i>
<span class="m-l-xs" translate>{{ 'app.admin.calendar.view_reservations' }}</span>
</span>
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,26 @@
<div class="modal-header">
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
<h1 translate>{{ 'app.admin.calendar.confirmation_required' }}</h1>
</div>
<div class="modal-body">
<p ng-hide="isRecurrent" translate>{{ 'app.admin.calendar.do_you_really_want_to_delete_this_slot' }}</p>
<p ng-show="isRecurrent" translate>{{ 'app.admin.calendar.delete_recurring_slot' }}</p>
<div ng-show="isRecurrent" class="form-group">
<label class="checkbox">
<input type="radio" name="delete_mode" ng-model="deleteMode" value="single" required/>
<span translate>{{ 'app.admin.calendar.delete_this_slot' }}</span>
</label>
<label class="checkbox">
<input type="radio" name="delete_mode" ng-model="deleteMode" value="next" required/>
<span translate>{{ 'app.admin.calendar.delete_this_and_next' }}</span>
</label>
<label class="checkbox">
<input type="radio" name="delete_mode" ng-model="deleteMode" value="all" required/>
<span translate>{{ 'app.admin.calendar.delete_all' }}</span>
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-info" ng-click="ok()" translate>{{ 'app.shared.buttons.delete' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -1,27 +1,27 @@
<div class="modal-header">
<h3 class="text-center red">
{{ 'admin_calendar.DATE_slot' | translate:{DATE:(start | amDateFormat: 'LL')} }} {{start | amDateFormat:'LT'}} - {{end | amDateFormat:'LT'}}
{{ 'app.admin.calendar.DATE_slot' | translate:{DATE:(start | amDateFormat: 'LL')} }} {{start | amDateFormat:'LT'}} - {{end | amDateFormat:'LT'}}
</h3>
</div>
<div class="modal-body" ng-show="step === 1">
<label class="m-t-sm" translate>{{ 'admin_calendar.what_kind_of_slot_do_you_want_to_create' }}</label>
<label class="m-t-sm" translate>{{ 'app.admin.calendar.what_kind_of_slot_do_you_want_to_create' }}</label>
<div class="form-group">
<div class="radio">
<label>
<input type="radio" id="training" name="available_type" value="training" ng-model="availability.available_type">
<span translate>{{ 'admin_calendar.training' }}</span>
<span translate>{{ 'app.admin.calendar.training' }}</span>
</label>
</div>
<div class="radio">
<label>
<input type="radio" id="machine" name="available_type" value="machines" ng-model="availability.available_type">
<span translate>{{ 'admin_calendar.machine' }}</span>
<span translate>{{ 'app.admin.calendar.machine' }}</span>
</label>
</div>
<div class="radio" ng-hide="fablabWithoutSpaces">
<label>
<input type="radio" id="space" name="available_type" value="space" ng-model="availability.available_type" ng-disabled="spaces.length === 0">
<span translate>{{ 'admin_calendar.space' }}</span>
<span translate>{{ 'app.admin.calendar.space' }}</span>
</label>
</div>
</div>
@ -29,7 +29,7 @@
<div class="modal-body" ng-show="step === 2">
<div ng-show="availability.available_type == 'machines'">
<p class="text-center font-sbold m-t-sm">{{ 'admin_calendar.select_some_machines' | translate }}</p>
<p class="text-center font-sbold m-t-sm">{{ 'app.admin.calendar.select_some_machines' | translate }}</p>
<div class="form-group m-l-xl">
<label class="checkbox" ng-repeat="machine in machines">
@ -43,7 +43,7 @@
</select>
<div class="row m-t">
<div class="form-group">
<label class="col-sm-6 control-label" for="nb_places_training" translate>{{ 'admin_calendar.number_of_tickets' }}</label>
<label class="col-sm-6 control-label" for="nb_places_training" translate>{{ 'app.admin.calendar.number_of_tickets' }}</label>
<div class="col-sm-6">
<input type="number" id="nb_places_training" class="form-control" ng-model="availability.nb_total_places">
</div>
@ -56,7 +56,7 @@
</select>
<div class="row m-t">
<div class="form-group">
<label class="col-sm-6 control-label" for="nb_places_space" translate>{{ 'admin_calendar.number_of_tickets' }}</label>
<label class="col-sm-6 control-label" for="nb_places_space" translate>{{ 'app.admin.calendar.number_of_tickets' }}</label>
<div class="col-sm-6">
<input type="number" id="nb_places_space" class="form-control" ng-model="availability.nb_total_places">
</div>
@ -66,19 +66,19 @@
</div>
<div class="modal-body" ng-show="step === 3">
<div id="timeAdjust" class="m-t-sm">
<p class="text-center font-sbold" translate>{{ 'admin_calendar.adjust_the_opening_hours' }}</p>
<p class="text-center font-sbold" translate>{{ 'app.admin.calendar.adjust_the_opening_hours' }}</p>
<div class="row">
<div class="col-md-3 col-md-offset-2">
<uib-timepicker ng-model="start" hour-step="timepickers.start.hstep" readonly-input="true" minute-step="timepickers.start.mstep" show-meridian="false"></uib-timepicker>
</div>
<span class="col-md-1 m-t-xl m-l" translate>{{ 'admin_calendar.to_time' }}</span>
<span class="col-md-1 m-t-xl m-l" translate>{{ 'app.admin.calendar.to_time' }}</span>
<fieldset ng-disabled="endDateReadOnly" class="col-md-5">
<uib-timepicker ng-model="end" hour-step="timepickers.end.hstep" readonly-input="true" minute-step="timepickers.end.mstep" show-meridian="false"></uib-timepicker>
</fieldset>
</div>
</div>
<div id="tagAssociate" class="m-t-lg">
<p class="text-center font-sbold" translate>{{ 'admin_calendar.restrict_this_slot_with_labels_optional' }}</p>
<p class="text-center font-sbold" translate>{{ 'app.admin.calendar.restrict_this_slot_with_labels_optional' }}</p>
<div class="row">
<div class="col-sm-12">
<ui-select multiple ng-model="availability.tag_ids" class="form-control">
@ -93,13 +93,89 @@
</div>
</div>
</div>
<div class="modal-footer" ng-show="step < 3">
<button class="btn btn-info" ng-click="previous()" ng-disabled="step === 1" translate>{{ 'admin_calendar.previous' }}</button>
<button class="btn btn-info" ng-click="next()" translate>{{ 'admin_calendar.next' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<div class="modal-body m-h" ng-show="step === 4">
<div class="m-t-sm">
<p class="text-center font-sbold" translate>{{ 'app.admin.calendar.recurrence' }}</p>
<div class="row">
<div class="form-group">
<label for="is_recurrent" translate>{{ 'app.admin.calendar.enabled' }}</label>
<input bs-switch
ng-model="availability.is_recurrent"
id="is_recurrent"
type="checkbox"
class="form-control"
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true"/>
<input type="hidden" name="availability[is_recurrent]" value="{{availability.is_recurrent}}"/>
</div>
<div class="modal-footer" ng-show="step === 3">
<button class="btn btn-info" ng-click="previous()" translate>{{ 'admin_calendar.previous' }}</button>
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
</div>
</div>
<div class="row">
<div class="form-group">
<label for="period">{{ 'app.admin.calendar.period' | translate }}</label>
<select id="period"
name="period"
class="form-control"
ng-model="availability.period"
ng-required="availability.is_recurrent"
ng-disabled="!availability.is_recurrent">
<option value="week" ng-selected="availability.period == 'week'" translate>{{ 'app.admin.calendar.week' }}</option>
<option value="month" ng-selected="availability.period == 'month'" translate>{{ 'app.admin.calendar.month' }}</option>
</select>
</div>
</div>
<div class="row">
<div class="form-group">
<label for="nb_periods">{{ 'app.admin.calendar.number_of_periods' | translate }}</label>
<input id="nb_periods"
name="nb_periods"
class="form-control"
ng-model="availability.nb_periods"
type="number"
ng-required="availability.is_recurrent"
ng-disabled="!availability.is_recurrent" />
</div>
</div>
<div class="row">
<div class="form-group">
<label for="end_date">{{ 'app.admin.calendar.end_date' | translate }}</label>
<input id="end_date"
name="end_date"
class="form-control"
ng-model="availability.end_date"
type="date"
ng-required="availability.is_recurrent"
ng-disabled="!availability.is_recurrent" />
</div>
</div>
</div>
<div class="modal-body m-h" ng-show="step === 5">
<div class="m-t-sm">
<p class="text-center font-sbold" translate>{{ 'app.admin.calendar.summary' }}</p>
<div class="row">
<span>{{ 'app.admin.calendar.about_to_create' | translate:{NUMBER:occurrences.length,TYPE:availability.available_type} }}</span>
<ul>
<li ng-repeat="slot in occurrences">{{slot.start_at | amDateFormat:'L LT'}} - {{slot.end_at | amDateFormat:'LT'}}</li>
</ul>
<div>
<span class="underline" translate>{{ 'app.admin.calendar.reservable' }}</span>
<span ng-bind-html="reservableName"></span>
</div>
<div class="m-t">
<span class="underline" translate>{{ 'app.admin.calendar.labels' }}</span>
<span ng-bind-html="tagsName"></span>
</div>
</div>
</div>
</div>
<div class="modal-footer" ng-show="step < 5">
<button class="btn btn-info" ng-click="previous()" ng-disabled="step === 1" translate>{{ 'app.admin.calendar.previous' }}</button>
<button class="btn btn-info" ng-click="next()" translate>{{ 'app.admin.calendar.next' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>
<div class="modal-footer" ng-show="step === 5">
<button class="btn btn-info" ng-click="previous()" translate>{{ 'app.admin.calendar.previous' }}</button>
<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>

View File

@ -0,0 +1,96 @@
<section class="heading b-b">
<div class="row no-gutter">
<div class="col-xs-2 col-sm-2 col-md-1">
<section class="heading-btn">
<a role="button" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
</section>
</div>
<div class="col-md-8 b-l b-r-md">
<section class="heading-title">
<h1 translate>{{ 'app.admin.icalendar.icalendar_import' }}</h1>
</section>
</div>
<div class="col-md-3">
<section class="heading-actions wrapper">
</section>
</div>
</div>
</section>
<section class="row no-gutter">
<div class="col-sm-12 col-md-12 col-lg-9">
<div class="alert alert-info m-lg" translate>
{{ 'app.admin.icalendar.intro' }}
</div>
<div class="wrapper-lg">
<table class="table" ng-show="calendars.length > 0">
<thead>
<tr>
<th style="width: 35%;" translate>{{ 'app.admin.icalendar.name' }}</th>
<th style="width: 35%;" translate>{{ 'app.admin.icalendar.url' }}</th>
<th translate>{{ 'app.admin.icalendar.display' }}</th>
<th style="width: 20%;"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="calendar in calendars">
<td class="calendar-name">{{calendar.name}}</td>
<td class="calendar-url"><a href="{{calendar.url}}" target="_blank">{{calendar.url}}</a></td>
<td class="calendar-legend-block text-left"><span class="calendar-legend" ng-style="calendarStyle(calendar)" translate> {{ calendar.text_hidden ? '' : 'app.admin.icalendar.example' }}</span>
<td class="calendar-actions">
<button class="btn btn-info" ng-click="sync(calendar)"><i class="fa fa-refresh"></i></button>
<button class="btn btn-danger" ng-click="delete(calendar)"><i class="fa fa-trash"></i></button>
</td>
</tr>
</tbody>
</table>
<form class="calendar-form" name="newImportForm">
<h4 translate>{{ 'app.admin.icalendar.new_import' }}</h4>
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-font"></i>
</div>
<input type="text" ng-model="newCalendar.name" class="form-control" placeholder="{{ 'app.admin.icalendar.name' | translate }}" required>
</div>
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-link"></i>
</div>
<input type="url" ng-model="newCalendar.url" class="form-control" placeholder="{{ 'app.admin.icalendar.url' | translate }}" required>
</div>
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-paint-brush"></i>
</div>
<input type="text" minicolors ng-model="newCalendar.color" class="form-control" placeholder="{{ 'app.admin.icalendar.color' | translate}}" required/>
</div>
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-paint-brush"></i>
</div>
<input type="text" minicolors ng-model="newCalendar.text_color" class="form-control" placeholder="{{ 'app.admin.icalendar.text_color' | translate}}" ng-required="!newCalendar.text_hidden"/>
</div>
<div class="input-group">
<label for="hideText" class="control-label m-r" translate>{{ 'app.admin.icalendar.hide_text' }}</label>
<input bs-switch
ng-model="newCalendar.text_hidden"
id="hideText"
type="checkbox"
class="form-control"
switch-on-text="{{ 'app.admin.icalendar.hidden' | translate }}"
switch-off-text="{{ 'app.admin.icalendar.shown' | translate }}"
switch-animate="true"/>
</div>
<div class="m-t text-right">
<button role="button" class="btn btn-warning" ng-click="save()" ng-disabled="newImportForm.$invalid" translate>
{{ 'app.shared.buttons.confirm' }}
</button>
</div>
</form>
</div>
</div>
</section>

View File

@ -1,15 +1,15 @@
<div class="form-group" ng-class="{'has-error': couponForm['coupon[name]'].$dirty && couponForm['coupon[name]'].$invalid}">
<label for="coupon[name]">{{ 'name' | translate }} *</label>
<label for="coupon[name]">{{ 'app.shared.coupon.name' | translate }} *</label>
<input type="text" id="coupon[name]"
name="coupon[name]"
class="form-control"
ng-model="coupon.name"
required="required"/>
<span class="help-block error" ng-show="couponForm['coupon[name]'].$dirty && couponForm['coupon[name]'].$error.required" translate>{{ 'name_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[name]'].$dirty && couponForm['coupon[name]'].$error.required" translate>{{ 'app.shared.coupon.name_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$invalid}">
<label for="coupon[code]">{{ 'code' | translate }} *</label>
<label for="coupon[code]">{{ 'app.shared.coupon.code' | translate }} *</label>
<input type="text" id="coupon[code]"
name="coupon[code]"
class="form-control"
@ -17,25 +17,25 @@
ng-pattern="/^[A-Z0-9\-]+$/"
ng-disabled="mode == 'EDIT'"
required="required"/>
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.required" translate>{{ 'code_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.pattern" translate>{{ 'code_must_be_composed_of_capital_letters_digits_and_or_dashes' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.required" translate>{{ 'app.shared.coupon.code_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$error.pattern" translate>{{ 'app.shared.coupon.code_must_be_composed_of_capital_letters_digits_and_or_dashes' }}</span>
</div>
<div class="form-group">
<label for="coupon[type]">{{ 'kind_of_coupon' | translate }} *</label>
<label for="coupon[type]">{{ 'app.shared.coupon.kind_of_coupon' | translate }} *</label>
<select id="coupon[type]"
name="coupon[type]"
class="form-control"
ng-model="coupon.type"
ng-disabled="mode == 'EDIT'"
required="required">
<option value="percent_off" translate>{{ 'percentage' }}</option>
<option value="amount_off" translate>{{ 'amount' }}</option>
<option value="percent_off" translate>{{ 'app.shared.coupon.percentage' }}</option>
<option value="amount_off" translate>{{ 'app.shared.coupon.amount' }}</option>
</select>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$invalid}" ng-show="coupon.type == 'percent_off'">
<label for="coupon[percent_off]">{{ 'percent_off' | translate }} *</label>
<label for="coupon[percent_off]">{{ 'app.shared.coupon.percent_off' | translate }} *</label>
<div class="input-group">
<input type="number" id="coupon[percent_off]"
name="coupon[percent_off]"
@ -47,13 +47,13 @@
ng-required="coupon.type == 'percent_off'"/>
<span class="input-group-addon"><i class="fa fa-percent"></i></span>
</div>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'percent_off_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'percentage_must_be_between_0_and_100' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'app.shared.coupon.percent_off_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'app.shared.coupon.percentage_must_be_between_0_and_100' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[amount_off]'].$dirty && couponForm['coupon[amount_off]'].$invalid}" ng-show="coupon.type == 'amount_off'">
<label for="coupon[amount_off]">{{ 'amount_off' | translate }} *</label>
<label for="coupon[amount_off]">{{ 'app.shared.coupon.amount_off' | translate }} *</label>
<div class="input-group">
<span class="input-group-addon">{{currencySymbol}}</span>
<input type="number" id="coupon[amount_off]"
@ -64,25 +64,25 @@
ng-disabled="mode == 'EDIT'"
ng-required="coupon.type == 'amount_off'"/>
</div>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'percent_off_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'percentage_must_be_between_0_and_100' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && couponForm['coupon[percent_off]'].$error.required" translate>{{ 'app.shared.coupon.percent_off_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[percent_off]'].$dirty && (couponForm['coupon[percent_off]'].$error.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'app.shared.coupon.percentage_must_be_between_0_and_100' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[validity_per_user]'].$dirty && couponForm['coupon[validity_per_user]'].$invalid}">
<label for="coupon[validity_per_user]">{{ 'validity_per_user' | translate }} *</label>
<label for="coupon[validity_per_user]">{{ 'app.shared.coupon.validity_per_user' | translate }} *</label>
<select id="coupon[validity_per_user]"
name="coupon[validity_per_user]"
class="form-control"
ng-model="coupon.validity_per_user"
required="required"
ng-disabled="mode == 'EDIT'"
ng-options="( validity | translate ) for validity in validities">
ng-options="validityName(validity) for validity in validities">
</select>
<span class="help-block error" ng-show="couponForm['coupon[validity_per_user]'].$dirty && couponForm['coupon[validity_per_user]'].$error.required" translate>{{ 'validity_per_user_is_required' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[validity_per_user]'].$dirty && couponForm['coupon[validity_per_user]'].$error.required" translate>{{ 'app.shared.coupon.validity_per_user_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': errors['valid_until']}">
<label for="coupon[valid_until]" translate>{{ 'valid_until' }}</label>
<label for="coupon[valid_until]" translate>{{ 'app.shared.coupon.valid_until' }}</label>
<div class="input-group">
<input type="text" id="coupon[valid_until]"
name="coupon[valid_until]"
@ -100,34 +100,34 @@
<span class="help-block error" ng-show="errors['valid_until']">{{ errors['valid_until'].join(' ; ') }}</span>
<span class="text-info text-xs">
<i class="fa fa-lightbulb-o"></i> {{ 'leave_empty_for_no_limit' | translate }}
<i class="fa fa-lightbulb-o"></i> {{ 'app.shared.coupon.leave_empty_for_no_limit' | translate }}
</span>
</div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[max_usages]'].$dirty && couponForm['coupon[max_usages]'].$invalid}">
<label for="coupon[max_usages]">{{ 'max_usages' | translate }}</label>
<label for="coupon[max_usages]">{{ 'app.shared.coupon.max_usages' | translate }}</label>
<input type="number" id="coupon[max_usages]"
name="coupon[max_usages]"
class="form-control"
ng-model="coupon.max_usages"
ng-disabled="mode == 'EDIT'"
min="0"/>
<span class="help-block error" ng-show="couponForm['coupon[max_usages]'].$dirty && couponForm['coupon[max_usages]'].$error.min" translate>{{ 'max_usages_must_be_equal_or_greater_than_0' }}</span>
<span class="help-block error" ng-show="couponForm['coupon[max_usages]'].$dirty && couponForm['coupon[max_usages]'].$error.min" translate>{{ 'app.shared.coupon.max_usages_must_be_equal_or_greater_than_0' }}</span>
<span class="text-info text-xs">
<i class="fa fa-lightbulb-o"></i> {{ 'leave_empty_for_no_limit' | translate }}
<i class="fa fa-lightbulb-o"></i> {{ 'app.shared.coupon.leave_empty_for_no_limit' | translate }}
</span>
</div>
<div class="form-group">
<label for="coupon[active]" translate>{{ 'enabled' }}</label>
<label for="coupon[active]" translate>{{ 'app.shared.coupon.enabled' }}</label>
<input bs-switch
ng-model="coupon.active"
id="coupon[active]"
type="checkbox"
class="form-control"
switch-on-text="{{ 'yes' | translate }}"
switch-off-text="{{ 'no' | translate }}"
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true" />
<input type="hidden" name="coupon[active]" value="{{coupon.active}}"/>
</div>

View File

@ -2,18 +2,18 @@
<div class="row no-gutter">
<div class="col-xs-2 col-sm-2 col-md-1">
<section class="heading-btn">
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
<a ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left pointer"></i></a>
</section>
</div>
<div class="col-xs-7 col-sm-7 col-md-8 b-l">
<section class="heading-title">
<h1>{{ 'coupon' | translate }} : {{ coupon.name }}</h1>
<h1>{{ 'app.admin.coupons_edit.coupon' | translate }} {{ coupon.name }}</h1>
</section>
</div>
<div class="col-xs-3 col-md-3">
<section class="heading-actions wrapper">
<a class="btn btn-lg btn-block btn-default m-t-xs" ui-sref="app.admin.pricing" translate>{{ 'cancel' }}</a>
<a class="btn btn-lg btn-block btn-default m-t-xs" ui-sref="app.admin.pricing" translate>{{ 'app.shared.buttons.cancel' }}</a>
</section>
</div>
@ -28,10 +28,10 @@
<div id="couponForm">
<form name="couponForm" novalidate="novalidate" class="col-lg-7 col-lg-offset-2 m-t-lg form-group">
<ng-include src="'<%= asset_path 'admin/coupons/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/coupons/_form.html" %>'"></ng-include>
<div class="panel-footer no-padder">
<input type="button" value="{{ 'confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="couponForm.$invalid" ng-click="updateCoupon()"/>
<input type="button" value="{{ 'app.shared.buttons.confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="couponForm.$invalid" ng-click="updateCoupon()"/>
</div>
</form>
</div>

View File

@ -2,12 +2,12 @@
<div class="row no-gutter">
<div class="col-xs-2 col-sm-2 col-md-1">
<section class="heading-btn">
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
<a ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left pointer"></i></a>
</section>
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'add_a_coupon' }}</h1>
<h1 translate>{{ 'app.admin.coupons_new.add_a_coupon' }}</h1>
</section>
</div>
@ -20,10 +20,10 @@
<div id="couponForm">
<form name="couponForm" novalidate="novalidate" class="col-lg-10 col-lg-offset-2 m-t-lg form-group">
<ng-include src="'<%= asset_path 'admin/coupons/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/coupons/_form.html" %>'"></ng-include>
<div class="panel-footer no-padder">
<input type="button" value="{{ 'save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="couponForm.$invalid" ng-click="saveCoupon()"/>
<input type="button" value="{{ 'app.shared.buttons.save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="couponForm.$invalid" ng-click="saveCoupon()"/>
</div>
</form>

View File

@ -1,11 +1,11 @@
<div class="m-t">
<h3 translate>{{ 'categories' }}</h3>
<p translate>{{ 'at_least_one_category_is_required' }}</p>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addElement('category')" translate>{{ 'add_a_category' }}</button>
<h3 translate>{{ 'app.admin.events.categories' }}</h3>
<p translate>{{ 'app.admin.events.at_least_one_category_is_required' }}</p>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addElement('category')" translate>{{ 'app.admin.events.add_a_category' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:80%" translate>{{ 'name' }}</th>
<th style="width:80%" translate>{{ 'app.admin.events.name' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -28,7 +28,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removeElement('category', $index)">
<i class="fa fa-trash-o"></i>
@ -39,12 +39,12 @@
</tbody>
</table>
<h3 translate>{{ 'themes' }}</h3>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addElement('theme')" translate>{{ 'add_a_theme' }}</button>
<h3 translate>{{ 'app.admin.events.themes' }}</h3>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addElement('theme')" translate>{{ 'app.admin.events.add_a_theme' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:80%" translate>{{ 'name' }}</th>
<th style="width:80%" translate>{{ 'app.admin.events.name' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -67,7 +67,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removeElement('theme', $index)">
<i class="fa fa-trash-o"></i>
@ -78,12 +78,12 @@
</tbody>
</table>
<h3 translate>{{ 'age_ranges' }}</h3>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addElement('age_range')" translate>{{ 'add_a_range' }}</button>
<h3 translate>{{ 'app.admin.events.age_ranges' }}</h3>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addElement('age_range')" translate>{{ 'app.admin.events.add_a_range' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:80%" translate>{{ 'name' }}</th>
<th style="width:80%" translate>{{ 'app.admin.events.name' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -106,7 +106,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removeElement('age_range', $index)">
<i class="fa fa-trash-o"></i>

View File

@ -7,13 +7,13 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
<section class="heading-title">
<h1 translate>{{ 'fablab_events' }}</h1>
<h1 translate>{{ 'app.admin.events.fablab_events' }}</h1>
</section>
</div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin'])">
<section class="heading-actions wrapper">
<a class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.events_new" role="button" translate>{{ 'add_an_event' }}</a>
<a class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.events_new" role="button" translate>{{ 'app.admin.events.add_an_event' }}</a>
</section>
</div>
</div>
@ -22,16 +22,16 @@
<div class="row">
<div class="col-md-12">
<uib-tabset justified="true">
<uib-tab heading="{{ 'events_monitoring' | translate }}">
<ng-include src="'<%= asset_path 'admin/events/monitoring.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.events.events_monitoring' | translate }}">
<ng-include src="'<%= asset_path "admin/events/monitoring.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'manage_filters' | translate }}">
<ng-include src="'<%= asset_path 'admin/events/filters.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.events.manage_filters' | translate }}">
<ng-include src="'<%= asset_path "admin/events/filters.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'manage_prices_categories' | translate }}">
<ng-include src="'<%= asset_path 'admin/events/prices.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.events.manage_prices_categories' | translate }}">
<ng-include src="'<%= asset_path "admin/events/prices.html" %>'"></ng-include>
</uib-tab>
</uib-tabset>
</div>

View File

@ -1,18 +1,18 @@
<div class="col-md-6 m-b m-t">
<select ng-model="eventsScope.selected" class="form-control" ng-change="changeScope()">
<option value="" translate>{{ 'all_events' }}</option>
<option value="passed" translate>{{ 'passed_events' }}</option>
<option value="future" translate>{{ 'events_to_come' }}</option>
<option value="future_asc" translate>{{ 'events_to_come_asc' }}</option>
<option value="" translate>{{ 'app.admin.events.all_events' }}</option>
<option value="passed" translate>{{ 'app.admin.events.passed_events' }}</option>
<option value="future" translate>{{ 'app.admin.events.events_to_come' }}</option>
<option value="future_asc" translate>{{ 'app.admin.events.events_to_come_asc' }}</option>
</select>
</div>
<table class="table">
<thead>
<tr>
<th style="width:30%" translate>{{ 'title' }}</th>
<th style="width:30%" translate>{{ 'dates' }}</th>
<th style="width:10%" translate>{{ 'booking' }}</th>
<th style="width:30%" translate>{{ 'app.admin.events.title' }}</th>
<th style="width:30%" translate>{{ 'app.admin.events.dates' }}</th>
<th style="width:10%" translate>{{ 'app.admin.events.booking' }}</th>
<th style="width:30%"></th>
</tr>
</thead>
@ -27,22 +27,22 @@
<!--One day event-->
<span ng-if="(event.start_date | amDateFormat:'LL')==(event.end_date | amDateFormat:'LL')">
{{ 'on_DATE' | translate:{DATE:(event.start_date | amDateFormat:'LL')} }}
{{ 'app.admin.events.on_DATE' | translate:{DATE:(event.start_date | amDateFormat:'LL')} }}
<span ng-if="event.all_day == 'false'">
{{ 'from_TIME' | translate:{TIME:(event.start_date | amDateFormat:'LT')} }}
<span class="text-sm font-thin" translate>{{ 'to_time' }}</span>
{{ 'app.admin.events.from_TIME' | translate:{TIME:(event.start_date | amDateFormat:'LT')} }}
<span class="text-sm font-thin" translate>{{ 'app.admin.events.to_time' }}</span>
{{event.end_date | amDateFormat:'LT'}}
</span>
</span>
<!--Multiple days event-->
<span ng-if="(event.start_date | amDateFormat:'LL')!=(event.end_date | amDateFormat:'LL')">
{{'from_DATE' | translate:{DATE:(event.start_date | amDateFormat:'LL')} }}
{{'to_date' | translate}} {{event.end_date | amDateFormat:'LL'}}
{{'app.admin.events.from_DATE' | translate:{DATE:(event.start_date | amDateFormat:'LL')} }}
{{'app.admin.events.to_date' | translate}} {{event.end_date | amDateFormat:'LL'}}
<br ng-if="event.all_day == 'false'"/>
<span ng-if="event.all_day == 'false'">
{{ 'from_TIME' | translate:{TIME:(event.start_date | amDateFormat:'LT')} }}
<span class="text-sm font-thin" translate>{{ 'to_time' }}</span>
{{ 'app.admin.events.from_TIME' | translate:{TIME:(event.start_date | amDateFormat:'LT')} }}
<span class="text-sm font-thin" translate>{{ 'app.admin.events.to_time' }}</span>
{{event.end_date | amDateFormat:'LT'}}
</span>
</span>
@ -50,17 +50,17 @@
<td style="vertical-align:middle">
<span class="ng-binding" ng-if="event.nb_total_places > 0">{{ event.nb_total_places - event.nb_free_places }} / {{ event.nb_total_places }}</span>
<span class="badge font-sbold cancelled" ng-if="event.nb_total_places == -1" translate>{{ 'cancelled' }}</span>
<span class="badge font-sbold" ng-if="!event.nb_total_places" translate>{{ 'free_entry' }}</span>
<span class="badge font-sbold cancelled" ng-if="event.nb_total_places == -1" translate>{{ 'app.admin.events.cancelled' }}</span>
<span class="badge font-sbold" ng-if="!event.nb_total_places" translate>{{ 'app.admin.events.free_entry' }}</span>
</td>
<td style="vertical-align:middle">
<div class="buttons">
<a class="btn btn-default" ui-sref="app.admin.event_reservations({id: event.id})">
<i class="fa fa-bookmark"></i> {{ 'view_reservations' | translate }}
<i class="fa fa-bookmark"></i> {{ 'app.admin.events.view_reservations' | translate }}
</a>
<a class="btn btn-default" ui-sref="app.admin.events_edit({id: event.id})">
<i class="fa fa-edit"></i> {{ 'edit' | translate }}
<i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}
</a>
</div>
</td>
@ -70,6 +70,6 @@
<div class="row">
<div class="col-lg-12 text-center">
<a class="btn btn-warning" ng-click="loadMoreEvents()" ng-if="paginateActive" translate>{{ 'load_the_next_events' }}</a>
<a class="btn btn-warning" ng-click="loadMoreEvents()" ng-if="paginateActive" translate>{{ 'app.admin.events.load_the_next_events' }}</a>
</div>
</div>

View File

@ -1,6 +1,6 @@
<div class="modal-header">
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
<h1 translate>{{ 'price_category' }}</h1>
<h1 translate>{{ 'app.admin.events.price_category' }}</h1>
</div>
<div class="modal-body">
<form role="form" name="priceCategoryForm" class="form-horizontal" novalidate autocomplete="off" ng-keydown="priceCategoryForm.$valid && $event.which == 13 && ok()">
@ -12,10 +12,10 @@
name="name"
ng-model="category.name"
class="form-control"
placeholder="{{ 'category_name' | translate }}"
placeholder="{{ 'app.admin.events.category_name' | translate }}"
required />
</div>
<span class="help-block" ng-show="priceCategoryForm.name.$dirty && priceCategoryForm.name.$error.required" translate>{{ 'category_name_is_required' }}</span>
<span class="help-block" ng-show="priceCategoryForm.name.$dirty && priceCategoryForm.name.$error.required" translate>{{ 'app.admin.events.category_name_is_required' }}</span>
</div>
</div>
@ -25,16 +25,16 @@
rows="10"
class="form-control"
id="conditions"
placeholder="{{ 'enter_here_the_conditions_under_which_this_price_is_applicable' | translate }}"
placeholder="{{ 'app.admin.events.enter_here_the_conditions_under_which_this_price_is_applicable' | translate }}"
name="conditions"
required>
</textarea>
<span class="help-block" ng-show="priceCategoryForm.conditions.$dirty && priceCategoryForm.conditions.$error.required" translate>{{ 'conditions_are_required' }}</span>
<span class="help-block" ng-show="priceCategoryForm.conditions.$dirty && priceCategoryForm.conditions.$error.required" translate>{{ 'app.admin.events.conditions_are_required' }}</span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-info" ng-click="ok()" ng-disabled="priceCategoryForm.$invalid" translate>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<button class="btn btn-info" ng-click="ok()" ng-disabled="priceCategoryForm.$invalid" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -1,12 +1,12 @@
<div class="m-t">
<h3 translate>{{ 'prices_categories' }}</h3>
<h3 translate>{{ 'app.admin.events.prices_categories' }}</h3>
<button type="button" class="btn btn-warning m-b m-t" ng-click="newPriceCategory()" translate>{{ 'add_a_price_category' }}</button>
<button type="button" class="btn btn-warning m-b m-t" ng-click="newPriceCategory()" translate>{{ 'app.admin.events.add_a_price_category' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:40%" translate>{{ 'name' }}</th>
<th style="width:40%" translate>{{ 'usages_count' }}</th>
<th style="width:40%" translate>{{ 'app.admin.events.name' }}</th>
<th style="width:40%" translate>{{ 'app.admin.events.usages_count' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -17,7 +17,7 @@
<td>
<div class="buttons">
<button class="btn btn-default" ng-click="editPriceCategory(category.id, $index)">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removePriceCategory(category.id, $index)">
<i class="fa fa-trash-o"></i>

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1>{{ 'the_reservations' | translate }} {{event.title}}</h1>
<h1>{{ 'app.admin.event_reservations.the_reservations' | translate }} {{event.title}}</h1>
</section>
</div>
</div>
@ -20,35 +20,36 @@
<table class="table" ng-if="reservations.length > 0">
<thead>
<tr>
<th style="width:25%" translate>{{ 'user' }}</th>
<th style="width:25%" translate>{{ 'payment_date' }}</th>
<th style="width:25%" translate>{{ 'reserved_tickets' }}</th>
<th style="width:25%" translate>{{ 'app.admin.event_reservations.user' }}</th>
<th style="width:25%" translate>{{ 'app.admin.event_reservations.payment_date' }}</th>
<th style="width:25%" translate>{{ 'app.admin.event_reservations.reserved_tickets' }}</th>
<th style="width:25%"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="reservation in reservations">
<tr ng-repeat="reservation in reservations" ng-class="{'disabled': isCancelled(reservation)}">
<td class="text-c">
<a ui-sref="app.logged.members_show({id: reservation.user_id})">{{ reservation.user_full_name }} </a>
</td>
<td>{{ reservation.created_at | amDateFormat:'LL LTS' }}</td>
<td>
<span ng-if="reservation.nb_reserve_places > 0">{{ 'full_price_' | translate }} {{reservation.nb_reserve_places}}<br/></span>
<span ng-if="reservation.nb_reserve_places > 0">{{ 'app.admin.event_reservations.full_price_' | translate }} {{reservation.nb_reserve_places}}<br/></span>
<span ng-repeat="ticket in reservation.tickets">{{ticket.event_price_category.price_category.name}} : {{ticket.booked}}</span>
<div ng-show="isCancelled(reservation)" class="canceled-marker" translate>{{ 'app.admin.event_reservations.canceled' }}</div>
</td>
<td>
<div class="buttons">
<button class="btn btn-default" ui-sref="app.public.events_show({id: event.id})">
<i class="fa fa-tag"></i> {{ 'show_the_event' | translate }}
<i class="fa fa-tag"></i> {{ 'app.admin.event_reservations.show_the_event' | translate }}
</button>
</div>
</td>
</tr>
</tbody>
</table>
<p ng-if="reservations.length == 0" translate>{{ 'no_reservations_for_now' }}</p>
<p ng-if="reservations.length == 0" translate>{{ 'app.admin.event_reservations.no_reservations_for_now' }}</p>
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.events" translate>{{ 'back_to_monitoring' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.events" translate>{{ 'app.admin.event_reservations.back_to_monitoring' }}</button>
</div>
</div>
</section>

View File

@ -1,13 +1,13 @@
<div class="m-t-lg m-b">
<button type="button" class="btn btn-warning" ng-click="addGroup()">
<i class="fa fa-plus m-r"></i>
<span translate>{{ 'group_form.add_a_group' }}</span>
<span translate>{{ 'app.admin.members.group_form.add_a_group' }}</span>
</button>
<div class="form-group pull-right">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<select ng-model="groupFiltering" class="form-control">
<option ng-repeat="status in filterDisabled" value="{{status}}" translate>{{ 'group_form.status_'+status }}</option>
<option ng-repeat="status in filterDisabled" value="{{status}}" translate>{{ 'app.admin.members.group_form.status_'+status }}</option>
</select>
</div>
</div>
@ -16,7 +16,7 @@
<table class="table">
<thead>
<tr>
<th style="width: 75%;" translate>{{ 'group_form.group_name' }}</th>
<th style="width: 75%;" translate>{{ 'app.admin.members.group_form.group_name' }}</th>
<th style="width: 25%"></th>
</tr>
</thead>
@ -39,11 +39,11 @@
</form>
<div class="buttons" ng-hide="rowform.$visible || group.slug === 'admins'">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-default" ng-click="toggleDisableGroup($index)">
<span ng-hide="group.disabled"><i class="fa fa-eye-slash"></i> <span translate>{{ 'group_form.disable' }}</span></span>
<span ng-show="group.disabled"><i class="fa fa-eye"></i> <span translate>{{ 'group_form.enable' }}</span></span>
<span ng-hide="group.disabled"><i class="fa fa-eye-slash"></i> <span translate>{{ 'app.admin.members.group_form.disable' }}</span></span>
<span ng-show="group.disabled"><i class="fa fa-eye"></i> <span translate>{{ 'app.admin.members.group_form.enable' }}</span></span>
</button>
<button class="btn btn-danger" ng-click="removeGroup($index)">
<i class="fa fa-trash-o"></i>

View File

@ -1,10 +1,10 @@
<ul>
<li><span class="period-info-title" translate>{{ 'invoices.closed_at' }}</span> : <span>{{period.closed_at | amDateFormat:'L'}}</span></li>
<li><span class="period-info-title" translate>{{ 'invoices.closed_by' }}</span> : <span>{{period.user_name}}</span></li>
<li><span class="period-info-title" translate>{{ 'invoices.period_total' }}</span> : <span>{{period.period_total | currency}}</span></li>
<li><span class="period-info-title" translate>{{ 'invoices.perpetual_total' }}</span> : <span>{{period.perpetual_total | currency}}</span></li>
<li><span class="period-info-title" translate>{{ 'app.admin.invoices.closed_at' }}</span> : <span>{{period.closed_at | amDateFormat:'L'}}</span></li>
<li><span class="period-info-title" translate>{{ 'app.admin.invoices.closed_by' }}</span> : <span>{{period.user_name}}</span></li>
<li><span class="period-info-title" translate>{{ 'app.admin.invoices.period_total' }}</span> : <span>{{period.period_total | currency}}</span></li>
<li><span class="period-info-title" translate>{{ 'app.admin.invoices.perpetual_total' }}</span> : <span>{{period.perpetual_total | currency}}</span></li>
<li>
<span class="period-info-title" translate>{{ 'invoices.integrity' }}</span> :
<span class="period-info-title" translate>{{ 'app.admin.invoices.integrity' }}</span> :
<i class="fa fa-link chained" ng-show="period.chained_footprint"></i>
<i class="fa fa-chain-broken broken" ng-hide="period.chained_footprint"></i>
</li>

View File

@ -1,11 +1,11 @@
<div class="modal-header">
<h3 class="text-center red" translate>{{ 'invoices.export_accounting_data' }}</h3>
<h3 class="text-center red" translate>{{ 'app.admin.invoices.export_accounting_data' }}</h3>
</div>
<div class="modal-body">
<form role="form" name="exportForm">
<div class="row">
<div class="form-group col-md-6">
<label for="start_date" translate>{{ 'invoices.export_form_date' }}</label>
<label for="start_date" translate>{{ 'app.admin.invoices.export_form_date' }}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text"
@ -23,7 +23,7 @@
</div>
</div>
<div class="form-group col-md-6">
<label for="end_date" translate>{{ 'invoices.export_to_date' }}</label>
<label for="end_date" translate>{{ 'app.admin.invoices.export_to_date' }}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text"
@ -42,36 +42,36 @@
</div>
</div>
<div class="row">
<h4 class="control-label m-l" translate>{{ 'invoices.export_to' }}</h4>
<h4 class="control-label m-l" translate>{{ 'app.admin.invoices.export_to' }}</h4>
<div class="form-group m-l-lg">
<label for="acd">
<input type="radio" name="acd" id="acd" ng-model="exportTarget.software" ng-value="'acd'" ng-click="fillSettings()" required/>
{{ 'invoices.acd' | translate }}
{{ 'app.admin.invoices.acd' | translate }}
</label>
</div>
</div>
<div class="row" ng-show="exportTarget.settings">
<div class="col-md-4 font-bold" translate>{{ 'invoices.format' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.format' }}</div>
<div class="col-md-8">{{ exportTarget.settings.format }}</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.encoding' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.encoding' }}</div>
<div class="col-md-8">{{ exportTarget.settings.encoding }}</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.separator' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.separator' }}</div>
<div class="col-md-8">{{ exportTarget.settings.separator }}</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.dateFormat' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.dateFormat' }}</div>
<div class="col-md-8">
<a href="https://apidock.com/ruby/DateTime/strftime" class="help-cursor" target="_blank">{{ exportTarget.settings.dateFormat }}</a>
</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.labelMaxLength' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.labelMaxLength' }}</div>
<div class="col-md-8">{{ exportTarget.settings.labelMaxLength }}</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.decimalSeparator' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.decimalSeparator' }}</div>
<div class="col-md-8">{{ exportTarget.settings.decimalSeparator }}</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.exportInvoicesAtZero' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.exportInvoicesAtZero' }}</div>
<div class="col-md-8" translate>{{ exportTarget.settings.exportInvoicesAtZero ? 'yes' : 'no' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'invoices.columns' }}</div>
<div class="col-md-4 font-bold" translate>{{ 'app.admin.invoices.columns' }}</div>
<table class="col-md-12 export-table-template">
<thead>
<tr>
<td ng-repeat="column in exportTarget.settings.columns" translate>{{ 'invoices.exportColumns.' + column }}</td>
<td ng-repeat="column in exportTarget.settings.columns" translate>{{ 'app.admin.invoices.exportColumns.' + column }}</td>
</tr>
</thead>
<tbody>
@ -91,7 +91,7 @@
<input name="type" type="hidden" ng-value="exportTarget.software"/>
<input name="key" type="hidden" ng-value="query.key"/>
<input name="query" type="hidden" ng-value="query.query"/>
<input type="submit" class="btn btn-warning" value="{{ 'confirm' | translate }}" formtarget="export-frame"/>
<input type="submit" class="btn btn-warning" value="{{ 'app.shared.buttons.confirm' | translate }}" formtarget="export-frame"/>
</form>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -1,10 +1,10 @@
<div class="modal-header">
<h3 class="text-center red" translate>{{ 'invoices.create_a_refund_on_this_invoice' }}</h3>
<h3 class="text-center red" translate>{{ 'app.admin.invoices.create_a_refund_on_this_invoice' }}</h3>
</div>
<div class="modal-body">
<form name="avoirForm" novalidate="novalidate">
<div class="form-group" ng-class="{'has-error': avoirForm.avoir_date.$dirty && avoirForm.avoir_date.$invalid }">
<label translate>{{ 'invoices.creation_date_for_the_refund' }}</label>
<label translate>{{ 'app.admin.invoices.creation_date_for_the_refund' }}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text"
@ -19,24 +19,24 @@
ng-click="openDatePicker($event)"
required/>
</div>
<span class="help-block" ng-show="avoirForm.avoir_date.$dirty && avoirForm.avoir_date.$error.required" translate>{{ 'invoices.creation_date_is_required' }}</span>
<span class="help-block" ng-show="avoirForm.avoir_date.$dirty && avoirForm.avoir_date.$error.required" translate>{{ 'app.admin.invoices.creation_date_is_required' }}</span>
</div>
<div class="form-group">
<label translate>{{ 'invoices.refund_mode' }}</label>
<label translate>{{ 'app.admin.invoices.refund_mode' }}</label>
<select class="form-control m-t-sm" name="payment_method" ng-model="avoir.payment_method" ng-options="mode.value as mode.name for mode in avoirModes" required></select>
</div>
<div class="form-group" ng-if="invoice.is_subscription_invoice">
<label translate>{{ 'invoices.do_you_want_to_disable_the_user_s_subscription' }}</label>
<label translate>{{ 'app.admin.invoices.do_you_want_to_disable_the_user_s_subscription' }}</label>
<select class="form-control m-t-sm" name="subscription_to_expire" ng-model="avoir.subscription_to_expire" ng-options="value as key for (key, value) in subscriptionExpireOptions" required></select>
</div>
<div ng-show="!invoice.is_subscription_invoice && invoice.items.length > 1" class="form-group">
<label translate>{{ 'invoices.elements_to_refund' }}</label>
<label translate>{{ 'app.admin.invoices.elements_to_refund' }}</label>
<table class="table partial-avoir-table">
<thead>
<tr>
<th class="input-col"></th>
<th class="label-col" translate>{{ 'invoices.description' }}</th>
<th class="amount-col" translate>{{ 'invoices.price' }}</th>
<th class="label-col" translate>{{ 'app.admin.invoices.description' }}</th>
<th class="amount-col" translate>{{ 'app.admin.invoices.price' }}</th>
</tr>
</thead>
<tbody>
@ -49,13 +49,13 @@
</table>
</div>
<div>
<label for="description" translate>{{ 'invoices.description_optional' }}</label>
<p translate>{{ 'invoices.will_appear_on_the_refund_invoice' }}</p>
<label for="description" translate>{{ 'app.admin.invoices.description_optional' }}</label>
<p translate>{{ 'app.admin.invoices.will_appear_on_the_refund_invoice' }}</p>
<textarea class="form-control m-t-sm" name="description" ng-model="avoir.description"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" ng-disabled="avoirForm.$invalid" translate>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<button class="btn btn-warning" ng-click="ok()" ng-disabled="avoirForm.$invalid" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -1,10 +1,10 @@
<div class="modal-header">
<h3 class="text-center red" translate>{{ 'invoices.close_accounting_period' }}</h3>
<h3 class="text-center red" translate>{{ 'app.admin.invoices.close_accounting_period' }}</h3>
</div>
<div class="modal-body">
<form name="closePeriodForm" novalidate="novalidate" class="row">
<div class="form-group col-md-6" ng-class="{'has-error': closePeriodForm.start_at.$dirty && closePeriodForm.start_at.$invalid }">
<label translate>{{ 'invoices.close_from_date' }}</label>
<label translate>{{ 'app.admin.invoices.close_from_date' }}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text"
@ -21,11 +21,11 @@
readonly
required/>
</div>
<span class="help-block" ng-show="closePeriodForm.start_at.$dirty && closePeriodForm.start_at.$error.required" translate>{{ 'invoices.start_date_is_required' }}</span>
<span class="help-block" ng-show="closePeriodForm.start_at.$dirty && closePeriodForm.start_at.$error.required" translate>{{ 'app.admin.invoices.start_date_is_required' }}</span>
<span class="help-block error" ng-show="errors.start_at">{{ errors.start_at[0] }}</span>
</div>
<div class="form-group col-md-6" ng-class="{'has-error': closePeriodForm.end_at.$dirty && closePeriodForm.end_at.$invalid }">
<label translate>{{ 'invoices.close_until_date' }}</label>
<label translate>{{ 'app.admin.invoices.close_until_date' }}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text"
@ -43,7 +43,7 @@
required
readonly/>
</div>
<span class="help-block" ng-show="closePeriodForm.end_at.$dirty && closePeriodForm.end_at.$error.required" translate>{{ 'invoices.end_date_is_required' }}</span>
<span class="help-block" ng-show="closePeriodForm.end_at.$dirty && closePeriodForm.end_at.$error.required" translate>{{ 'app.admin.invoices.end_date_is_required' }}</span>
<span class="help-block error" ng-show="errors.end_at">{{ errors.end_at[0] }}</span>
</div>
</form>
@ -51,12 +51,12 @@
<span class="help-block error">{{ $parent.invoiceErrorRE.exec(key)[1] }} : {{ value[0] }}</span>
</div>
<div>
<h4 translate>{{ 'invoices.previous_closings' }}</h4>
<h4 translate>{{ 'app.admin.invoices.previous_closings' }}</h4>
<table class="table closings-table" ng-show="accountingPeriods.length > 0">
<thead>
<tr>
<th translate>{{ 'invoices.start_date' }}</th>
<th translate>{{ 'invoices.end_date' }}</th>
<th translate>{{ 'app.admin.invoices.start_date' }}</th>
<th translate>{{ 'app.admin.invoices.end_date' }}</th>
<th></th>
</tr>
</thead>
@ -72,10 +72,10 @@
</tr>
</tbody>
</table>
<div ng-show="accountingPeriods.length === 0" translate>{{ 'invoices.no_periods'}}</div>
<div ng-show="accountingPeriods.length === 0" translate>{{ 'app.admin.invoices.no_periods'}}</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" ng-disabled="closePeriodForm.$invalid || pendingCreation" translate>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" ng-disabled="pendingCreation" translate>{{ 'cancel' }}</button>
<button class="btn btn-warning" ng-click="ok()" ng-disabled="closePeriodForm.$invalid || pendingCreation" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" ng-disabled="pendingCreation" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -7,14 +7,14 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'invoices.invoices' }}</h1>
<h1 translate>{{ 'app.admin.invoices.invoices' }}</h1>
</section>
</div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
<section class="heading-actions wrapper">
<a class="btn btn-default rounded m-t-sm" ng-click="toggleExportModal()"><i class="fa fa-book"></i></a>
<iframe name="export-frame" height="0" width="0" class="none" id="accounting-export-frame"></iframe>
<a class="btn btn-lg btn-default rounded m-t-sm text-sm" ng-click="closeAnAccountingPeriod()"><i class="fa fa-calendar-check-o"></i> {{ 'invoices.accounting_periods' | translate }}</a>
<a class="btn btn-lg btn-default rounded m-t-sm text-sm" ng-click="closeAnAccountingPeriod()"><i class="fa fa-calendar-check-o"></i> {{ 'app.admin.invoices.accounting_periods' | translate }}</a>
</section>
</div>
</div>
@ -24,14 +24,14 @@
<div class="row">
<div class="col-md-12">
<uib-tabset justified="true">
<uib-tab heading="{{ 'invoices.invoices_list' | translate }}" ng-hide="fablabWithoutInvoices" active="tabs.listing.active">
<h3 class="m-t-xs"><i class="fa fa-filter"></i> {{ 'invoices.filter_invoices' | translate }}</h3>
<uib-tab heading="{{ 'app.admin.invoices.invoices_list' | translate }}" ng-hide="fablabWithoutInvoices" active="tabs.listing.active">
<h3 class="m-t-xs"><i class="fa fa-filter"></i> {{ 'app.admin.invoices.filter_invoices' | translate }}</h3>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon" translate>{{ 'invoices.invoice_num_' }}</span>
<span class="input-group-addon" translate>{{ 'app.admin.invoices.invoice_num_' }}</span>
<input type="text" ng-model="searchInvoice.reference" class="form-control" placeholder="" ng-change="handleFilterChange()">
</div>
</div>
@ -40,7 +40,7 @@
<div class="col-md-4">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon" translate>{{ 'invoices.customer_' }}</span>
<span class="input-group-addon" translate>{{ 'app.admin.invoices.customer_' }}</span>
<input type="text" ng-model="searchInvoice.name" class="form-control" placeholder="" ng-change="handleFilterChange()">
</div>
</div>
@ -49,7 +49,7 @@
<div class="col-md-4">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">{{ "invoices.date_" | translate }}</span>
<span class="input-group-addon">{{ "app.admin.invoices.date_" | translate }}</span>
<input type="date" ng-model="searchInvoice.date" class="form-control" ng-change="handleFilterChange()">
</div>
</div>
@ -64,13 +64,13 @@
<thead>
<tr>
<th style="width:5%"></th>
<th style="width:15%"><a href="" ng-click="setOrderInvoice('reference')">{{ 'invoices.invoice_num' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='reference', 'fa fa-sort-numeric-desc': orderInvoice=='-reference', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderInvoice('reference')">{{ 'app.admin.invoices.invoice_num' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='reference', 'fa fa-sort-numeric-desc': orderInvoice=='-reference', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:20%"><a href="" ng-click="setOrderInvoice('date')">{{ 'invoices.date' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='date', 'fa fa-sort-numeric-desc': orderInvoice=='-date', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:20%"><a href="" ng-click="setOrderInvoice('date')">{{ 'app.admin.invoices.date' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='date', 'fa fa-sort-numeric-desc': orderInvoice=='-date', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:10%"><a href="" ng-click="setOrderInvoice('total')"> {{ 'invoices.price' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='total', 'fa fa-sort-numeric-desc': orderInvoice=='-total', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:10%"><a href="" ng-click="setOrderInvoice('total')"> {{ 'app.admin.invoices.price' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='total', 'fa fa-sort-numeric-desc': orderInvoice=='-total', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:20%"><a href="" ng-click="setOrderInvoice('name')">{{ 'invoices.customer' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderInvoice=='name', 'fa fa-sort-alpha-desc': orderInvoice=='-name', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:20%"><a href="" ng-click="setOrderInvoice('name')">{{ 'app.admin.invoices.customer' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderInvoice=='name', 'fa fa-sort-alpha-desc': orderInvoice=='-name', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
<th style="width:30%"></th>
</tr>
@ -91,13 +91,13 @@
<td>
<div class="buttons">
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="!invoice.is_avoir">
<i class="fa fa-file-pdf-o"></i> {{ 'invoices.download_the_invoice' | translate }}
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.invoices.download_the_invoice' | translate }}
</a>
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="invoice.is_avoir">
<i class="fa fa-file-pdf-o"></i> {{ 'invoices.download_the_credit_note' | translate }}
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.invoices.download_the_credit_note' | translate }}
</a>
<a class="btn btn-default" ng-click="generateAvoirForInvoice(invoice)" ng-if="(!invoice.has_avoir || invoice.has_avoir == 'partial') && !invoice.is_avoir && !invoice.prevent_refund && !isDateClosed(invoice.created_at)">
<i class="fa fa-reply"></i> {{ 'invoices.credit_note' | translate }}
<i class="fa fa-reply"></i> {{ 'app.admin.invoices.credit_note' | translate }}
</a>
</div>
</td>
@ -105,9 +105,9 @@
</tbody>
</table>
<div class="text-center">
<button class="btn btn-warning" ng-click="showNextInvoices()" ng-hide="noMoreResults"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'invoices.display_more_invoices' | translate }}</button>
<button class="btn btn-warning" ng-click="showNextInvoices()" ng-hide="noMoreResults"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'app.admin.invoices.display_more_invoices' | translate }}</button>
</div>
<p ng-if="invoices.length == 0" translate>{{ 'invoices.no_invoices_for_now' }}</p>
<p ng-if="invoices.length == 0" translate>{{ 'app.admin.invoices.no_invoices_for_now' }}</p>
</div>
</div>
@ -116,10 +116,10 @@
<uib-tab heading="{{ 'invoices.invoicing_settings' | translate }}" active="tabs.settings.active">
<uib-tab heading="{{ 'app.admin.invoices.invoicing_settings' | translate }}" active="tabs.settings.active">
<div class="alert alert-warning p-md m-t" role="alert" ng-show="fablabWithoutInvoices">
<i class="fa fa-warning m-r"></i>
<span translate>{{ 'invoices.warning_invoices_disabled' }}</span>
<span translate>{{ 'app.admin.invoices.warning_invoices_disabled' }}</span>
</div>
<form class="invoice-placeholder">
<div class="invoice-logo">
@ -128,79 +128,79 @@
<div class="tools-box">
<div class="btn-group">
<div class="btn btn-default btn-file">
<i class="fa fa-edit"></i> {{ 'invoices.change_logo' | translate }}
<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>{{ 'invoices.john_smith' }}</strong>
<div translate>{{ 'invoices.john_smith_at_example_com' }}</div>
<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()">{{ 'invoices.invoice_reference_' | translate }} {{mkReference()}}</div>
<div class="invoice-code invoice-editable" ng-show="invoice.code.active" ng-click="openEditCode()">{{ 'invoices.code_' | translate }} {{invoice.code.model}}</div>
<div class="invoice-code invoice-activable" ng-show="!invoice.code.active" ng-click="openEditCode()" translate>{{ 'invoices.code_disabled' }}</div>
<div class="invoice-order invoice-editable" ng-click="openEditInvoiceNb()"> {{ 'invoices.order_num' | translate }} {{mkNumber()}}</div>
<div class="invoice-date">{{ 'invoices.invoice_issued_on_DATE_at_TIME' | translate:{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT')} }}</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">
{{ 'invoices.object_reservation_of_john_smith_on_DATE_at_TIME' | translate:{DATE:(inOneWeek | amDateFormat:'L'), TIME:(inOneWeek | amDateFormat:'LT')} }}
{{ '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">
{{ 'invoices.order_summary' | translate }}
{{ 'app.admin.invoices.order_summary' | translate }}
<table>
<thead>
<tr>
<th translate>{{ 'invoices.details' }}</th>
<th class="right" translate>{{ 'invoices.amount' }}</th>
<th translate>{{ 'app.admin.invoices.details' }}</th>
<th class="right" translate>{{ 'app.admin.invoices.amount' }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ 'invoices.machine_booking-3D_printer' | translate }} {{inOneWeek | amDateFormat:'LLL'}} - {{inOneWeekAndOneHour | amDateFormat:'LT'}}</td>
<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>{{ 'invoices.total_amount' }}</td>
<td ng-show="invoice.VAT.active" translate>{{ 'invoices.total_including_all_taxes' }}</td>
<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>{{ 'invoices.VAT_disabled' }}</td>
<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>{{ 'invoices.including_VAT' | translate }} {{invoice.VAT.rate}} %</td>
<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>{{ 'invoices.including_total_excluding_taxes' }}</td>
<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>{{ 'invoices.including_amount_payed_on_ordering' }}</td>
<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)}">
{{ 'invoices.settlement_by_debit_card_on_DATE_at_TIME_for_an_amount_of_AMOUNT' }}
{{ '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": "{{ "invoices.important_notes" | translate }}",
"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": "{{ "invoices.address_and_legal_information" | translate }}",
"placeholder": "{{ "app.admin.invoices.address_and_legal_information" | translate }}",
"buttons": ["bold", "underline"]
}'
ng-blur="legalsEditEnd($event)">
@ -211,116 +211,116 @@
<uib-tab heading="{{ 'invoices.accounting_codes' | translate }}">
<uib-tab heading="{{ 'app.admin.invoices.accounting_codes' | translate }}">
<div class="panel panel-default m-t-md accounting-codes">
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<label for="journalCode" translate>{{ 'invoices.accounting_journal_code' }}</label>
<input type="text" id="journalCode" ng-model="settings.journalCode.value" class="form-control" placeholder="{{ 'invoices.general_journal_code' | translate }}"/>
<label for="journalCode" translate>{{ 'app.admin.invoices.accounting_journal_code' }}</label>
<input type="text" id="journalCode" ng-model="settings.journalCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_journal_code' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="cardClientCode" translate>{{ 'invoices.accounting_card_client_code' }}</label>
<input type="text" id="cardClientCode" ng-model="settings.cardClientCode.value" class="form-control" placeholder="{{ 'invoices.card_client_code' | translate }}" />
<label for="cardClientCode" translate>{{ 'app.admin.invoices.accounting_card_client_code' }}</label>
<input type="text" id="cardClientCode" ng-model="settings.cardClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="cardClientLabel" translate>{{ 'invoices.accounting_card_client_label' }}</label>
<input type="text" id="cardClientLabel" ng-model="settings.cardClientLabel.value" class="form-control" placeholder="{{ 'invoices.card_client_label' | translate }}"/>
<label for="cardClientLabel" translate>{{ 'app.admin.invoices.accounting_card_client_label' }}</label>
<input type="text" id="cardClientLabel" ng-model="settings.cardClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="walletClientCode" translate>{{ 'invoices.accounting_wallet_client_code' }}</label>
<input type="text" id="walletClientCode" ng-model="settings.walletClientCode.value" class="form-control" placeholder="{{ 'invoices.wallet_client_code' | translate }}" />
<label for="walletClientCode" translate>{{ 'app.admin.invoices.accounting_wallet_client_code' }}</label>
<input type="text" id="walletClientCode" ng-model="settings.walletClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="walletClientLabel" translate>{{ 'invoices.accounting_wallet_client_label' }}</label>
<input type="text" id="walletClientLabel" ng-model="settings.walletClientLabel.value" class="form-control" placeholder="{{ 'invoices.wallet_client_label' | translate }}"/>
<label for="walletClientLabel" translate>{{ 'app.admin.invoices.accounting_wallet_client_label' }}</label>
<input type="text" id="walletClientLabel" ng-model="settings.walletClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="otherClientCode" translate>{{ 'invoices.accounting_other_client_code' }}</label>
<input type="text" id="otherClientCode" ng-model="settings.otherClientCode.value" class="form-control" placeholder="{{ 'invoices.other_client_code' | translate }}" />
<label for="otherClientCode" translate>{{ 'app.admin.invoices.accounting_other_client_code' }}</label>
<input type="text" id="otherClientCode" ng-model="settings.otherClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="otherClientLabel" translate>{{ 'invoices.accounting_other_client_label' }}</label>
<input type="text" id="otherClientLabel" ng-model="settings.otherClientLabel.value" class="form-control" placeholder="{{ 'invoices.other_client_label' | translate }}"/>
<label for="otherClientLabel" translate>{{ 'app.admin.invoices.accounting_other_client_label' }}</label>
<input type="text" id="otherClientLabel" ng-model="settings.otherClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="walletCode" translate>{{ 'invoices.accounting_wallet_code' }}</label>
<input type="text" id="walletCode" ng-model="settings.walletCode.value" class="form-control" placeholder="{{ 'invoices.general_wallet_code' | translate }}" />
<label for="walletCode" translate>{{ 'app.admin.invoices.accounting_wallet_code' }}</label>
<input type="text" id="walletCode" ng-model="settings.walletCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="walletLabel" translate>{{ 'invoices.accounting_wallet_label' }}</label>
<input type="text" id="walletLabel" ng-model="settings.walletLabel.value" class="form-control" placeholder="{{ 'invoices.general_wallet_label' | translate }}"/>
<label for="walletLabel" translate>{{ 'app.admin.invoices.accounting_wallet_label' }}</label>
<input type="text" id="walletLabel" ng-model="settings.walletLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="vatCode" translate>{{ 'invoices.accounting_vat_code' }}</label>
<input type="text" id="vatCode" ng-model="settings.vatCode.value" class="form-control" placeholder="{{ 'invoices.general_vat_code' | translate }}"/>
<label for="vatCode" translate>{{ 'app.admin.invoices.accounting_vat_code' }}</label>
<input type="text" id="vatCode" ng-model="settings.vatCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_code' | translate }}"/>
</div>
<div class="col-md-6">
<label for="vatLabel" translate>{{ 'invoices.accounting_vat_label' }}</label>
<input type="text" id="vatLabel" ng-model="settings.vatLabel.value" class="form-control" placeholder="{{ 'invoices.general_vat_label' | translate }}"/>
<label for="vatLabel" translate>{{ 'app.admin.invoices.accounting_vat_label' }}</label>
<input type="text" id="vatLabel" ng-model="settings.vatLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="subscriptionCode" translate>{{ 'invoices.accounting_subscription_code' }}</label>
<input type="text" id="subscriptionCode" ng-model="settings.subscriptionCode.value" class="form-control" placeholder="{{ 'invoices.general_subscription_code' | translate }}" />
<label for="subscriptionCode" translate>{{ 'app.admin.invoices.accounting_subscription_code' }}</label>
<input type="text" id="subscriptionCode" ng-model="settings.subscriptionCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="subscriptionLabel" translate>{{ 'invoices.accounting_subscription_label' }}</label>
<input type="text" id="subscriptionLabel" ng-model="settings.subscriptionLabel.value" class="form-control" placeholder="{{ 'invoices.general_subscription_label' | translate }}"/>
<label for="subscriptionLabel" translate>{{ 'app.admin.invoices.accounting_subscription_label' }}</label>
<input type="text" id="subscriptionLabel" ng-model="settings.subscriptionLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="machineCode" translate>{{ 'invoices.accounting_Machine_code' }}</label>
<input type="text" id="machineCode" ng-model="settings.machineCode.value" class="form-control" placeholder="{{ 'invoices.general_machine_code' | translate }}"/>
<label for="machineCode" translate>{{ 'app.admin.invoices.accounting_Machine_code' }}</label>
<input type="text" id="machineCode" ng-model="settings.machineCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_code' | translate }}"/>
</div>
<div class="col-md-6">
<label for="machineLabel" translate>{{ 'invoices.accounting_Machine_label' }}</label>
<input type="text" id="machineLabel" ng-model="settings.machineLabel.value" class="form-control" placeholder="{{ 'invoices.general_machine_label' | translate }}"/>
<label for="machineLabel" translate>{{ 'app.admin.invoices.accounting_Machine_label' }}</label>
<input type="text" id="machineLabel" ng-model="settings.machineLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="trainingCode" translate>{{ 'invoices.accounting_Training_code' }}</label>
<input type="text" id="trainingCode" ng-model="settings.trainingCode.value" class="form-control" placeholder="{{ 'invoices.general_training_code' | translate }}" />
<label for="trainingCode" translate>{{ 'app.admin.invoices.accounting_Training_code' }}</label>
<input type="text" id="trainingCode" ng-model="settings.trainingCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="trainingLabel" translate>{{ 'invoices.accounting_Training_label' }}</label>
<input type="text" id="trainingLabel" ng-model="settings.trainingLabel.value" class="form-control" placeholder="{{ 'invoices.general_training_label' | translate }}"/>
<label for="trainingLabel" translate>{{ 'app.admin.invoices.accounting_Training_label' }}</label>
<input type="text" id="trainingLabel" ng-model="settings.trainingLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="eventCode" translate>{{ 'invoices.accounting_Event_code' }}</label>
<input type="text" id="eventCode" ng-model="settings.eventCode.value" class="form-control" placeholder="{{ 'invoices.general_event_code' | translate }}"/>
<label for="eventCode" translate>{{ 'app.admin.invoices.accounting_Event_code' }}</label>
<input type="text" id="eventCode" ng-model="settings.eventCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_code' | translate }}"/>
</div>
<div class="col-md-6">
<label for="eventLabel" translate>{{ 'invoices.accounting_Event_label' }}</label>
<input type="text" id="eventLabel" ng-model="settings.eventLabel.value" class="form-control" placeholder="{{ 'invoices.general_event_label' | translate }}"/>
<label for="eventLabel" translate>{{ 'app.admin.invoices.accounting_Event_label' }}</label>
<input type="text" id="eventLabel" ng-model="settings.eventLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_label' | translate }}"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label for="spaceCode" translate>{{ 'invoices.accounting_Space_code' }}</label>
<input type="text" id="spaceCode" ng-model="settings.spaceCode.value" class="form-control" placeholder="{{ 'invoices.general_space_code' | translate }}" />
<label for="spaceCode" translate>{{ 'app.admin.invoices.accounting_Space_code' }}</label>
<input type="text" id="spaceCode" ng-model="settings.spaceCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_code' | translate }}" />
</div>
<div class="col-md-6">
<label for="spaceLabel" translate>{{ 'invoices.accounting_Space_label' }}</label>
<input type="text" id="spaceLabel" ng-model="settings.spaceLabel.value" class="form-control" placeholder="{{ 'invoices.general_space_label' | translate }}"/>
<label for="spaceLabel" translate>{{ 'app.admin.invoices.accounting_Space_label' }}</label>
<input type="text" id="spaceLabel" ng-model="settings.spaceLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_label' | translate }}"/>
</div>
</div>
<button name="button" class="btn btn-warning m-t-lg" ng-click="save()" translate>{{ 'save' }}</button>
<button name="button" class="btn btn-warning m-t-lg" ng-click="save()" translate>{{ 'app.shared.buttons.save' }}</button>
</div>
</div>
</uib-tab>
@ -333,125 +333,125 @@
<script type="text/ng-template" id="editReference.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'invoices.invoice_reference' }}</h3>
<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'">{{ 'invoices.year' | translate }}</li>
<li ng-click="invoice.reference.help = 'addMonth.html'">{{ 'invoices.month' | translate }}</li>
<li ng-click="invoice.reference.help = 'addDay.html'">{{ 'invoices.day' | translate }}</li>
<li ng-click="invoice.reference.help = 'addInvoiceNumber.html'">{{ 'invoices.num_of_invoice' | translate }}</li>
<li ng-click="invoice.reference.help = 'addOnlineInfo.html'">{{ 'invoices.online_sales' | translate }}</li>
<%# <li ng-click="invoice.reference.help = 'addWalletInfo.html'">{{ 'invoices.wallet' | translate }}</li> %>
<li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'invoices.refund' | translate }}</li>
<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>{{ 'invoices.model' }}</h4>
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
<input type="text" class="form-control" ng-model="model">
</div>
<div class="help">
<h4 translate>{{ 'invoices.documentation' }}</h4>
<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>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<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>{{ 'invoices.2_digits_year' }}</td></tr>
<tr><td><strong>YYYY</strong></td><td translate>{{ 'invoices.4_digits_year' }}</td></tr>
<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>{{ 'invoices.month_number' }}</td></tr>
<tr><td><strong>MM</strong></td><td translate>{{ 'invoices.2_digits_month_number' }}</td></tr>
<tr><td><strong>MMM</strong></td><td translate>{{ 'invoices.3_characters_month_name' }}</td></tr>
<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>{{ 'invoices.day_in_the_month' }}</td></tr>
<tr><td><strong>DD</strong></td><td translate>{{ 'invoices.2_digits_day_in_the_month' }}</td></tr>
<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>{{ 'invoices.n_digits_daily_count_of_invoices' }}</td></tr>
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'invoices.n_digits_monthly_count_of_invoices' }}</td></tr>
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'invoices.n_digits_annual_amount_of_invoices' }}</td></tr>
<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>{{ 'invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
<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>{{ 'invoices.n_digits_count_of_orders' }}</td></tr>
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'invoices.n_digits_daily_count_of_orders' }}</td></tr>
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'invoices.n_digits_monthly_count_of_orders' }}</td></tr>
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'invoices.n_digits_annual_amount_of_orders' }}</td></tr>
<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>{{ 'invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
<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>{{ 'invoices.add_a_notice_regarding_the_online_sales_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'invoices.eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe' | translate }}</td></tr>
<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>{{ 'invoices.add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'invoices.eg_WPM_will_add_PM_to_the_invoices_settled_with_wallet' | translate }}</td></tr>
<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>{{ 'invoices.add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned' | translate }}<mark translate>{{ 'invoices.this_will_never_be_added_when_an_online_sales_notice_is_present' }}</mark> {{ 'invoices.eg_RA_will_add_A_to_the_refund_invoices' | translate }}</td></tr>
<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>{{ 'invoices.code' }}</h3>
<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>{{ 'invoices.enable_the_code' }}</label>
<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="{{ 'invoices.enabled' | translate }}"
switch-off-text="{{ 'invoices.disabled' | translate }}"
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>{{ 'invoices.code' }}</label>
<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>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<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>
@ -461,33 +461,33 @@
<script type="text/ng-template" id="editNumber.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'invoices.order_number' }}</h3>
<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>{{ 'invoices.elements' }}</h4>
<h4 translate>{{ 'app.admin.invoices.elements' }}</h4>
<ul>
<li ng-click="invoice.number.help = 'addYear.html'">{{ 'invoices.year' | translate }}</li>
<li ng-click="invoice.number.help = 'addMonth.html'">{{ 'invoices.month' | translate }}</li>
<li ng-click="invoice.number.help = 'addDay.html'">{{ 'invoices.day' | translate }}</li>
<li ng-click="invoice.number.help = 'addOrderNumber.html'">{{ 'invoices.order_num' | translate }}</li>
<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>{{ 'invoices.model' }}</h4>
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
<input type="text" class="form-control" ng-model="model">
</div>
<div class="help">
<h4 translate>{{ 'invoices.documentation' }}</h4>
<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>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<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>
@ -496,23 +496,23 @@
<script type="text/ng-template" id="editVAT.html">
<div class="custom-invoice">
<div class="modal-header">
<h3 class="modal-title" translate>{{ 'invoices.VAT' }}</h3>
<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>{{ 'invoices.enable_VAT' }}</label>
<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="{{ 'invoices.enabled' | translate }}"
switch-off-text="{{ 'invoices.disabled' | translate }}"
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>{{ 'invoices.VAT_rate' }}</label>
<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"/>
@ -520,32 +520,32 @@
</div>
<div class="m-t-lg">
<h4 translate>{{ 'invoices.VAT_history' }}</h4>
<h4 translate>{{ 'app.admin.invoices.VAT_history' }}</h4>
<table class="table scrollable-3-cols">
<thead>
<tr>
<th translate>{{ 'invoices.VAT_rate' }}</th>
<th translate>{{ 'invoices.changed_at' }}</th>
<th translate>{{ 'invoices.changed_by' }}</th>
<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>{{'invoices.VAT_disabled'}}</span>
<span class="no-user-label" ng-show="value.enabled === true" translate>{{'invoices.VAT_enabled'}}</span>
<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>{{ 'invoices.deleted_user' }}</span></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>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<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,18 +1,18 @@
<div class="form-group" ng-class="{'has-error': userForm['user[group_id]'].$dirty && userForm['user[group_id]'].$invalid}">
<label for="user_group_id" class="col-sm-3 control-label">
<span translate>{{ 'group' }}</span>
<span translate>{{ 'app.shared.user_admin.group' }}</span>
<span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span>
</label>
<div class="col-sm-9">
<select ng-model="user.group_id" ng-disabled="user.subscribed_plan" class="form-control" name="user[group_id]" id="user_group_id" ng-options="g.id as g.name for g in groups" required>
</select>
<input type="hidden" name="user[group_id]" ng-value="user.group_id" />
<span class="help-block" ng-show="userForm['user[group_id]'].$dirty && userForm['user[group_id]'].$error.required" translate>{{ 'group_is_required' }}</span>
<span class="help-block" ng-show="userForm['user[group_id]'].$dirty && userForm['user[group_id]'].$error.required" translate>{{ 'app.shared.user_admin.group_is_required' }}</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" translate>{{ 'trainings' }}</label>
<label class="col-sm-2 control-label" translate>{{ 'app.shared.user_admin.trainings' }}</label>
<div class="col-sm-10">
<input type="hidden" name="user[statistic_profile_attributes][training_ids][]" value="" />
<ui-select multiple ng-model="user.training_ids" class="form-control">
@ -28,7 +28,7 @@
</div>
<div class="form-group">
<label class="col-sm-2 control-label" translate>{{ 'tags' }}</label>
<label class="col-sm-2 control-label" translate>{{ 'app.shared.user_admin.tags' }}</label>
<div class="col-sm-10">
<input type="hidden" name="user[tag_ids][]" value="" />
<ui-select multiple ng-model="user.tag_ids" name="user[tag_ids][]" class="form-control">

View File

@ -2,23 +2,23 @@
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'search_for_an_administrator' | translate }}">
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'app.admin.members.search_for_an_administrator' | translate }}">
</div>
</div>
</div>
<div class="col-md-12">
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.admins_new" translate>{{ 'add_a_new_administrator' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.admins_new" translate>{{ 'app.admin.members.add_a_new_administrator' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.last_name')">{{ 'surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.last_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.last_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.last_name')">{{ 'app.admin.members.surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.last_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.last_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.first_name')">{{ 'first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.first_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.first_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.first_name')">{{ 'app.admin.members.first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.first_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.first_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('email')">{{ 'email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='email', 'fa fa-sort-alpha-desc': orderAdmin =='-email', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('email')">{{ 'app.admin.members.email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='email', 'fa fa-sort-alpha-desc': orderAdmin =='-email', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:10%"><a href="" ng-click="setOrderAdmin('profile_attributes.phone')">{{ 'phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderAdmin =='profile_attributes.phone', 'fa fa-sort-numeric-desc': orderAdmin =='-profile_attributes.phone', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:10%"><a href="" ng-click="setOrderAdmin('profile_attributes.phone')">{{ 'app.admin.members.phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderAdmin =='profile_attributes.phone', 'fa fa-sort-numeric-desc': orderAdmin =='-profile_attributes.phone', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:10%"></th>
</tr>
</thead>

View File

@ -9,8 +9,8 @@
</div>
<div class="col-md-8 b-l b-r">
<section class="heading-title">
<h1 class="inline">{{ 'user' | translate }} {{ user.name }}</h1>
<span class="label label-danger text-white" ng-show="user.need_completion" translate>{{ 'incomplete_profile' }}</span>
<h1 class="inline">{{ 'app.shared.user_admin.user' | translate }} {{ user.name }}</h1>
<span class="label label-danger text-white" ng-show="user.need_completion" translate>{{ 'app.shared.user_admin.incomplete_profile' }}</span>
</section>
</div>
@ -18,7 +18,7 @@
<div class="col-md-3">
<section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }}
{{ 'app.shared.buttons.cancel' }}
</div>
</section>
@ -34,11 +34,11 @@
<uib-tabset justified="true" class="m-t">
<uib-tab heading="{{ 'user_profile' | translate }}">
<uib-tab heading="{{ 'app.shared.user_admin.user_profile' | translate }}">
<section class="panel panel-danger m-lg" ng-show="user.need_completion && activeProvider.providable_type !== 'DatabaseProvider'">
<div class="panel-body m-r" translate>
{{ 'warning_incomplete_user_profile_probably_imported_from_sso' }}
{{ 'app.shared.user_admin.warning_incomplete_user_profile_probably_imported_from_sso' }}
</div>
</section>
@ -46,21 +46,21 @@
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<ng-include src="'<%= asset_path 'shared/_member_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "shared/_member_form.html" %>'"></ng-include>
<ng-include src="'<%= asset_path 'admin/members/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/members/_form.html" %>'"></ng-include>
</div> <!-- ./panel-body -->
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
<input type="submit" value="{{ 'app.shared.buttons.confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
</div>
</section>
</form>
</uib-tab>
<uib-tab heading="{{ 'subscription' | translate }}" ng-if="!fablabWithoutPlans">
<uib-tab heading="{{ 'app.admin.members_edit.subscription' | translate }}" ng-if="!fablabWithoutPlans">
<section class="panel panel-default bg-light m-lg">
@ -68,24 +68,24 @@
<div class="" ng-show="subscription">
<h3>{{ subscription.plan | humanReadablePlanName }}</h3>
<p>
{{ 'duration' | translate }} {{ subscription.plan.interval | planIntervalFilter: subscription.plan.interval_count }}
{{ 'app.admin.members_edit.duration' | translate }} {{ subscription.plan.interval | planIntervalFilter: subscription.plan.interval_count }}
</p>
<p>
{{ 'expires_at' | translate }} {{ subscription.expired_at | amDateFormat: 'L' }}
{{ 'app.admin.members_edit.expires_at' | translate }} {{ subscription.expired_at | amDateFormat: 'L' }}
</p>
<p>
{{ 'price_' | translate }} {{ subscription.plan.amount | currency}}
{{ 'app.admin.members_edit.price_' | translate }} {{ subscription.plan.amount | currency}}
</p>
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, true)" translate>{{ 'offer_free_days' }}</button>
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, false)" translate>{{ 'extend_subscription' }}</button>
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, true)" translate>{{ 'app.admin.members_edit.offer_free_days' }}</button>
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, false)" translate>{{ 'app.admin.members_edit.extend_subscription' }}</button>
</div>
<div class="" ng-hide="subscription">
<p translate>
{{ 'user_has_no_current_subscription' }}
{{ 'app.admin.members_edit.user_has_no_current_subscription' }}
</p>
<button class="btn btn-default" ng-click="createSubscriptionModal(user, plans.filter(filterDisabledPlans))" translate>{{ 'subscribe_to_a_plan' }}</button>
<button class="btn btn-default" ng-click="createSubscriptionModal(user, plans.filter(filterDisabledPlans))" translate>{{ 'app.admin.members_edit.subscribe_to_a_plan' }}</button>
</div>
</div>
@ -93,11 +93,11 @@
</section>
</uib-tab>
<uib-tab heading="{{ 'trainings' | translate }}">
<uib-tab heading="{{ 'app.admin.members_edit.trainings' | translate }}">
<div class="col-md-4">
<div class="widget panel b-a m-t-lg">
<div class="panel-heading b-b ">
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'next_trainings' | translate }}</h4>
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'app.admin.members_edit.next_trainings' | translate }}</h4>
</div>
<div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.training_reservations.length > 0">
@ -105,14 +105,14 @@
<span class="font-sbold">{{r.reservable.name}}</span> - <span class="label label-warning wrapper-sm">{{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }}</span>
</li>
</ul>
<div ng-if="user.training_reservations.length == 0" translate>{{ 'no_trainings' }}</div>
<div ng-if="user.training_reservations.length == 0" translate>{{ 'app.admin.members_edit.no_trainings' }}</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="widget panel b-a m-t-lg">
<div class="panel-heading b-b">
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'passed_trainings' | translate }}</h4>
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'app.admin.members_edit.passed_trainings' | translate }}</h4>
</div>
<div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.training_reservations.length > 0">
@ -125,14 +125,14 @@
</div> -->
</li>
</ul>
<div ng-if="user.training_reservations.length == 0" translate>{{ 'no_trainings' }}</div>
<div ng-if="user.training_reservations.length == 0" translate>{{ 'app.admin.members_edit.no_trainings' }}</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="widget panel b-a m-t-lg">
<div class="panel-heading b-b">
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'validated_trainings' | translate }}</h4>
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'app.admin.members_edit.validated_trainings' | translate }}</h4>
</div>
<div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.trainings.length > 0">
@ -140,17 +140,17 @@
<span class="font-sbold">{{t.name}}</span>
</li>
</ul>
<div ng-if="user.trainings.length == 0" translate>{{ 'no_trainings' }}</div>
<div ng-if="user.trainings.length == 0" translate>{{ 'app.admin.members_edit.no_trainings' }}</div>
</div>
</div>
</div>
</uib-tab>
<uib-tab heading="{{ 'events' | translate }}">
<uib-tab heading="{{ 'app.admin.members_edit.events' | translate }}">
<div class="col-md-6">
<div class="widget panel b-a m m-t-lg">
<div class="panel-heading b-b">
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'next_events' | translate }}</h4>
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'app.admin.members_edit.next_events' | translate }}</h4>
</div>
<div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.events_reservations.length > 0">
@ -158,22 +158,22 @@
<a class="font-sbold" ui-sref="app.public.events_show({id: r.reservable.id})">{{r.reservable.title}}</a> - <span class="label label-warning wrapper-sm">{{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }}</span>
<span ng-if="r.nb_reserve_places > 0">
<br/>
<span translate translate-values="{ NUMBER: r.nb_reserve_places}" translate-interpolation="messageformat">{{ 'NUMBER_full_price_tickets_reserved' }}</span>
<span translate translate-values="{ NUMBER: r.nb_reserve_places }">{{ 'app.admin.members_edit.NUMBER_full_price_tickets_reserved' }}</span>
</span>
<span ng-repeat="ticket in r.tickets">
<br/>
<span translate translate-values="{ NUMBER: ticket.booked, NAME: ticket.price_category.name }" translate-interpolation="messageformat">{{ 'NUMBER_NAME_tickets_reserved' }}</span>
<span translate translate-values="{ NUMBER: ticket.booked, NAME: ticket.price_category.name }">{{ 'app.admin.members_edit.NUMBER_NAME_tickets_reserved' }}</span>
</span>
</li>
</ul>
<div ng-if="(user.events_reservations | eventsReservationsFilter:'future').length == 0" translate>{{ 'no_upcoming_events' }}</div>
<div ng-if="(user.events_reservations | eventsReservationsFilter:'future').length == 0" translate>{{ 'app.admin.members_edit.no_upcoming_events' }}</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="widget panel b-a m m-t-lg">
<div class="panel-heading b-b">
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'passed_events' | translate }}</h4>
<h4 class="text-u-c"><i class="fa fa-tag m-r-xs"></i> {{ 'app.admin.members_edit.passed_events' | translate }}</h4>
</div>
<div class="widget-content bg-light auto wrapper r-b">
<ul class="list-unstyled" ng-if="user.events_reservations.length > 0">
@ -181,22 +181,22 @@
<span class="font-sbold">{{r.reservable.title}}</span> - <span class="label label-info text-white wrapper-sm">{{ r.start_at | amDateFormat:'LLL' }} - {{ r.end_at | amDateFormat:'LT' }}</span>
</li>
</ul>
<div ng-if="(user.events_reservations | eventsReservationsFilter:'passed').length == 0" translate>{{ 'no_passed_events' }}</div>
<div ng-if="(user.events_reservations | eventsReservationsFilter:'passed').length == 0" translate>{{ 'app.admin.members_edit.no_passed_events' }}</div>
</div>
</div>
</div>
</uib-tab>
<uib-tab heading="{{ 'invoices' | translate }}" ng-hide="fablabWithoutInvoices">
<uib-tab heading="{{ 'app.admin.members_edit.invoices' | translate }}" ng-hide="fablabWithoutInvoices">
<div class="col-md-12 m m-t-lg">
<table class="table" ng-if="user.invoices.length > 0">
<thead>
<tr>
<th style="width:25%" translate>{{ 'invoice_num' }}</th>
<th style="width:25%" translate>{{ 'date' }}</th>
<th style="width:25%" translate>{{ 'price' }}</th>
<th style="width:25%" translate>{{ 'app.admin.members_edit.invoice_num' }}</th>
<th style="width:25%" translate>{{ 'app.admin.members_edit.date' }}</th>
<th style="width:25%" translate>{{ 'app.admin.members_edit.price' }}</th>
<th style="width:25%"></th>
</tr>
</thead>
@ -209,34 +209,34 @@
<td>
<div class="buttons">
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="!invoice.is_avoir">
<i class="fa fa-file-pdf-o"></i> {{ 'download_the_invoice' | translate }}
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.members_edit.download_the_invoice' | translate }}
</a>
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="invoice.is_avoir">
<i class="fa fa-file-pdf-o"></i> {{ 'download_the_refund_invoice' | translate }}
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.members_edit.download_the_refund_invoice' | translate }}
</a>
</div>
</td>
</tr>
</tbody>
</table>
<p ng-if="user.invoices.length == 0" translate>{{ 'no_invoices_for_now' }}</p>
<p ng-if="user.invoices.length == 0" translate>{{ 'app.admin.members_edit.no_invoices_for_now' }}</p>
</div>
</uib-tab>
<uib-tab heading="{{ 'wallet' | translate }}">
<uib-tab heading="{{ 'app.admin.members_edit.wallet' | translate }}">
<div class="col-md-12 m m-t-lg">
<ng-include src="'<%= asset_path 'wallet/show.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "wallet/show.html" %>'"></ng-include>
<div class="clearfix"></div>
<div class="col-sm-4 text-center">
<button type="button" class="btn btn-warning m-t m-b" ng-click="createWalletCreditModal(user, wallet)" translate>{{ 'to_credit' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ng-click="createWalletCreditModal(user, wallet)" translate>{{ 'app.admin.members_edit.to_credit' }}</button>
</div>
</div>
<div class="col-md-12 m m-t-lg">
<ng-include src="'<%= asset_path 'wallet/transactions.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "wallet/transactions.html" %>'"></ng-include>
</div>
</uib-tab>
</uib-tabset>

View File

@ -11,14 +11,14 @@
<div class="col-md-8 b-l b-r">
<section class="heading-title">
<h1 translate>{{ 'members_import.import_members' }}</h1>
<h1 translate>{{ 'app.admin.members_import.import_members' }}</h1>
</section>
</div>
<div class="col-md-3">
<section class="heading-actions wrapper">
<a class="btn btn-lg btn-block btn-default m-t-xs" target="_blank" href="example.csv" translate>
{{ 'members_import.download_example' }}
{{ 'app.admin.members_import.download_example' }}
</a>
</section>
</div>
@ -29,7 +29,7 @@
<div class="row p-sm">
<div class="col-md-12">
<p class="alert alert-info" translate>
{{ 'members_import.info' }}
{{ 'app.admin.members_import.info' }}
</p>
</div>
</div>
@ -37,12 +37,12 @@
<div class="row m-h-sm">
<div class="col-md-6 p-h-s">
<h3 translate>{{ 'members_import.groups' }}</h3>
<h3 translate>{{ 'app.admin.members_import.groups' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.group_name' }}</th>
<th translate>{{ 'members_import.group_identifier' }}</th>
<th translate>{{ 'app.admin.members_import.group_name' }}</th>
<th translate>{{ 'app.admin.members_import.group_identifier' }}</th>
</tr>
</thead>
<tbody>
@ -59,12 +59,12 @@
</div>
<div class="col-md-6 p-h-s">
<h3 translate>{{ 'members_import.trainings' }}</h3>
<h3 translate>{{ 'app.admin.members_import.trainings' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.training_name' }}</th>
<th translate>{{ 'members_import.training_identifier' }}</th>
<th translate>{{ 'app.admin.members_import.training_name' }}</th>
<th translate>{{ 'app.admin.members_import.training_identifier' }}</th>
</tr>
</thead>
<tbody>
@ -86,12 +86,12 @@
<div class="row m-h-sm">
<div class="col-md-6 p-h-s" ng-hide="tags.length == 0">
<h3 translate>{{ 'members_import.tags' }}</h3>
<h3 translate>{{ 'app.admin.members_import.tags' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.tag_name' }}</th>
<th translate>{{ 'members_import.tag_identifier' }}</th>
<th translate>{{ 'app.admin.members_import.tag_name' }}</th>
<th translate>{{ 'app.admin.members_import.tag_identifier' }}</th>
</tr>
</thead>
<tbody>
@ -119,10 +119,10 @@
<div class="m-t">
<p class="alert alert-warning m-h" translate>
{{ 'members_import.required_fields' }}
{{ 'app.admin.members_import.required_fields' }}
</p>
<p class="alert alert-warning m-h" translate>
{{ 'members_import.about_example' }}
{{ 'app.admin.members_import.about_example' }}
</p>
</div>
@ -130,8 +130,8 @@
<div class="form-control" data-trigger="fileinput">
<i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">{{file.attachment}}</span>
</div>
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new" translate>{{ 'members_import.select_file' }}</span>
<span class="fileinput-exists" translate>{{ 'change' }}</span>
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new" translate>{{ 'app.admin.members_import.select_file' }}</span>
<span class="fileinput-exists" translate>{{ 'app.shared.buttons.change' }}</span>
<input type="file"
name="import_members"
accept="text/csv"
@ -140,23 +140,23 @@
</div>
<div class="m-h">
<span translate>{{ 'members_import.update_field' }}</span>
<span translate>{{ 'app.admin.members_import.update_field' }}</span>
<div class="radio m-l-md">
<label class="control-label">
<input type="radio" id="update_field" name="update_field" value="id" checked>
<span translate>{{ 'members_import.update_on_id' }}</span>
<span translate>{{ 'app.admin.members_import.update_on_id' }}</span>
</label>
</div>
<div class="radio m-l-md">
<label class="control-label">
<input type="radio" id="update_field" name="update_field" value="username">
<span translate>{{ 'members_import.update_on_username' }}</span>
<span translate>{{ 'app.admin.members_import.update_on_username' }}</span>
</label>
</div>
<div class="radio m-l-md">
<label class="control-label">
<input type="radio" id="update_field" name="update_field" value="email">
<span translate>{{ 'members_import.update_on_email' }}</span>
<span translate>{{ 'app.admin.members_import.update_on_email' }}</span>
</label>
</div>
</div>
@ -165,7 +165,7 @@
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'members_import.import' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="importForm.$invalid"/>
<input type="submit" value="{{ 'app.admin.members_import.import' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="importForm.$invalid"/>
</div>
</section>

View File

@ -11,7 +11,7 @@
<div class="col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'members_import_result.import_results' }}</h1>
<h1 translate>{{ 'app.admin.members_import_result.import_results' }}</h1>
</section>
</div>
@ -22,11 +22,11 @@
<div class="row no-gutter">
<div class="col-sm-12 col-md-12">
<h2 class="m-l-lg">{{ 'members_import_result.import_details' | translate:{DATE:(import.created_at | amDateFormat:'L'), USER:import.user.full_name, ID:import.id} }}</h2>
<h2 class="m-l-lg">{{ 'app.admin.members_import_result.import_details' | translate:{DATE:(import.created_at | amDateFormat:'L'), USER:import.user.full_name, ID:import.id} }}</h2>
<h3 class="m-l-lg" ng-hide="results"><i class="fa fa-spinner fa-pulse"></i> <span translate>{{ 'members_import_result.pending' }}</span></h3>
<h3 class="m-l-lg" ng-hide="results"><i class="fa fa-spinner fa-pulse"></i> <span translate>{{ 'app.admin.members_import_result.pending' }}</span></h3>
<div ng-show="results">
<h3 class="m-l-lg" translate>{{ 'members_import_result.results' }}</h3>
<h3 class="m-l-lg" translate>{{ 'app.admin.members_import_result.results' }}</h3>
<div class="row p-h-lg" ng-repeat="resultRow in results track by $index">
<div class="scroll-x">
@ -41,21 +41,21 @@
</div>
<div ng-if="resultRow.status">
<i class="fa fa-arrow-right m-l-lg m-r"></i>
<span class="m-r-md">{{ 'members_import_result.status_' + resultRow.status | translate:{ID:resultRow.user} }}</span>
<span class="m-r-md">{{ 'app.admin.members_import_result.status_' + resultRow.status | translate:{ID:resultRow.user} }}</span>
<span ng-show="resultRow.result" class="green font-bold">
<i class="fa fa-check-square-o fa-stack-outside"></i>
<span class="m-l" translate>{{ 'members_import_result.success' }}</span>
<span class="m-l" translate>{{ 'app.admin.members_import_result.success' }}</span>
</span>
<span ng-hide="resultRow.result" class="text-red-only font-bold">
<span class="fa-stack v-bottom">
<i class="fa fa-square-o fa-stack-1x fa-stack-outside"></i>
<i class="fa fa-times fa-stack-1x fa-stack-inside"></i>
</span>
<span class="m-l" translate>{{ 'members_import_result.failed' }}</span>
<span class="m-l" translate>{{ 'app.admin.members_import_result.failed' }}</span>
</span>
</div>
<div class="m-l-lg text-red-only" ng-if="!resultRow.row && !resultRow.status">
<span class="m-r" translate>{{ 'members_import_result.error_details' }}</span>{{resultRow}}
<span class="m-r" translate>{{ 'app.admin.members_import_result.error_details' }}</span>{{resultRow}}
</div>
</div>
</div>

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-8 col-sm-8 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'users_management' }}</h1>
<h1 translate>{{ 'app.admin.members.users_management' }}</h1>
</section>
</div>
<div class="col-xs-1 col-xs-offset-1 col-md-offset-2 b-l">
@ -26,23 +26,23 @@
<div class="col-md-12">
<uib-tabset justified="true">
<uib-tab heading="{{ 'members' | translate }}">
<ng-include src="'<%= asset_path 'admin/members/members.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.members.members' | translate }}">
<ng-include src="'<%= asset_path "admin/members/members.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'administrators' | translate }}">
<ng-include src="'<%= asset_path 'admin/members/administrators.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.members.administrators' | translate }}">
<ng-include src="'<%= asset_path "admin/members/administrators.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'groups' | translate }}">
<uib-tab heading="{{ 'app.admin.members.groups' | translate }}">
<div ui-view="groups"></div>
</uib-tab>
<uib-tab heading="{{ 'tags' | translate }}">
<uib-tab heading="{{ 'app.admin.members.tags' | translate }}">
<div ui-view="tags"></div>
</uib-tab>
<uib-tab heading="{{ 'authentication' | translate }}">
<uib-tab heading="{{ 'app.admin.members.authentication' | translate }}">
<div ui-view="authentification"></div>
</uib-tab>
</uib-tabset>

View File

@ -2,21 +2,21 @@
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<input type="text" ng-model="member.searchText" class="form-control" placeholder="{{ 'search_for_an_user' | translate }}" ng-change="updateTextSearch()">
<input type="text" ng-model="member.searchText" class="form-control" placeholder="{{ 'app.admin.members.search_for_an_user' | translate }}" ng-change="updateTextSearch()">
</div>
</div>
</div>
<div class="col-md-12">
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.members_new" translate>{{ 'add_a_new_member' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.members_new" translate>{{ 'app.admin.members.add_a_new_member' }}</button>
<div class="pull-right">
<a class="btn btn-default" ng-href="api/members/export_members.xlsx" target="export-frame" ng-click="alertExport('members')">
<i class="fa fa-file-excel-o"></i> {{ 'members' | translate }}
<i class="fa fa-file-excel-o"></i> {{ 'app.admin.members.members' | translate }}
</a>
<a class="btn btn-default" ng-href="api/members/export_subscriptions.xlsx" target="export-frame" ng-if="!fablabWithoutPlans" ng-click="alertExport('subscriptions')">
<i class="fa fa-file-excel-o"></i> {{ 'subscriptions' | translate }}
<i class="fa fa-file-excel-o"></i> {{ 'app.admin.members.subscriptions' | translate }}
</a>
<a class="btn btn-default" ng-href="api/members/export_reservations.xlsx" target="export-frame" ng-click="alertExport('reservations')">
<i class="fa fa-file-excel-o"></i> {{ 'reservations' | translate }}
<i class="fa fa-file-excel-o"></i> {{ 'app.admin.members.reservations' | translate }}
</a>
<iframe name="export-frame" height="0" width="0" class="none"></iframe>
</div>
@ -25,12 +25,12 @@
<table class="table">
<thead>
<tr>
<th style="width:15%"><a href="" ng-click="setOrderMember('last_name')">{{ 'surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='last_name', 'fa fa-sort-alpha-desc': member.order=='-last_name', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderMember('first_name')">{{ 'first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='first_name', 'fa fa-sort-alpha-desc': member.order=='-first_name', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%" class="hidden-xs"><a href="" ng-click="setOrderMember('email')">{{ 'email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='email', 'fa fa-sort-alpha-desc': member.order=='-email', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:10%" class="hidden-xs hidden-sm hidden-md"><a href="" ng-click="setOrderMember('phone')">{{ 'phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': member.order=='phone', 'fa fa-sort-numeric-desc': member.order=='-phone', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:20%" class="hidden-xs hidden-sm"><a href="" ng-click="setOrderMember('group')">{{ 'user_type' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='group', 'fa fa-sort-alpha-desc': member.order=='-group', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%" class="hidden-xs hidden-sm hidden-md"><a href="" ng-click="setOrderMember('plan')">{{ 'subscription' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='plan', 'fa fa-sort-alpha-desc': member.order=='-plan', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderMember('last_name')">{{ 'app.admin.members.surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='last_name', 'fa fa-sort-alpha-desc': member.order=='-last_name', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderMember('first_name')">{{ 'app.admin.members.first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='first_name', 'fa fa-sort-alpha-desc': member.order=='-first_name', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%" class="hidden-xs"><a href="" ng-click="setOrderMember('email')">{{ 'app.admin.members.email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='email', 'fa fa-sort-alpha-desc': member.order=='-email', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:10%" class="hidden-xs hidden-sm hidden-md"><a href="" ng-click="setOrderMember('phone')">{{ 'app.admin.members.phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': member.order=='phone', 'fa fa-sort-numeric-desc': member.order=='-phone', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:20%" class="hidden-xs hidden-sm"><a href="" ng-click="setOrderMember('group')">{{ 'app.admin.members.user_type' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='group', 'fa fa-sort-alpha-desc': member.order=='-group', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:15%" class="hidden-xs hidden-sm hidden-md"><a href="" ng-click="setOrderMember('plan')">{{ 'app.admin.members.subscription' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': member.order=='plan', 'fa fa-sort-alpha-desc': member.order=='-plan', 'fa fa-arrows-v': member.order }"></i></a></th>
<th style="width:10%"></th>
</tr>
</thead>
@ -45,15 +45,18 @@
<td>
<div class="buttons">
<button class="btn btn-default" ui-sref="app.admin.members_edit({id: m.id})">
<i class="fa fa-edit"></i> {{ 'edit' | translate }}
<i class="fa fa-edit"></i>
</button>
<span class="label label-danger text-white" ng-show="m.need_completion" translate>{{ 'incomplete_profile' }}</span>
<button class="btn btn-danger" ng-click="deleteMember(m.id)">
<i class="fa fa-trash"></i>
</button>
<span class="label label-danger text-white" ng-show="m.need_completion" translate>{{ 'app.admin.members.incomplete_profile' }}</span>
</div>
</td>
</tr>
</tbody>
</table>
<div class="text-center">
<button class="btn btn-warning" ng-click="showNextMembers()" ng-hide="member.noMore"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'display_more_users' | translate }}</button>
<button class="btn btn-warning" ng-click="showNextMembers()" ng-hide="member.noMore"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'app.admin.members.display_more_users' | translate }}</button>
</div>
</div>

View File

@ -9,7 +9,7 @@
</div>
<div class="col-md-8 b-l b-r">
<section class="heading-title">
<h1 translate>{{ 'members_new.add_a_member' }}</h1>
<h1 translate>{{ 'app.admin.members_new.add_a_member' }}</h1>
</section>
</div>
@ -17,7 +17,7 @@
<div class="col-md-3">
<section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }}
{{ 'app.shared.buttons.cancel' }}
</div>
</section>
@ -45,19 +45,19 @@
ng-model="user.organization"
ng-change="toggleOrganization()"
value="false"/>
<label for="organization" translate>{{ 'members_new.user_is_an_organization' }}</label>
<label for="organization" translate>{{ 'app.admin.members_new.user_is_an_organization' }}</label>
</div>
</div>
</div>
<ng-include src="'<%= asset_path 'shared/_member_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "shared/_member_form.html" %>'"></ng-include>
<ng-include src="'<%= asset_path 'admin/members/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/members/_form.html" %>'"></ng-include>
</div> <!-- ./panel-body -->
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
<input type="submit" value="{{ 'app.shared.buttons.save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
</div>
</section>
</form>

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-7 b-l b-r-md">
<section class="heading-title">
<h1 translate>{{ 'open_api_clients' }}</h1>
<h1 translate>{{ 'app.admin.open_api_clients.open_api_clients' }}</h1>
</section>
</div>
@ -15,7 +15,7 @@
<section class="heading-actions wrapper">
<a href="<%= apipie_apipie_path({version: 'v1'}) %>" target="_blank" class="btn btn-info b-2x rounded m-t-sm">
<i class="fa fa-book" aria-hidden="true"></i>&nbsp;
<span translate>{{ 'api_documentation' }}</span>&nbsp;
<span translate>{{ 'app.admin.open_api_clients.api_documentation' }}</span>&nbsp;
<span class="exponent"><i class="fa fa-external-link" aria-hidden="true"></i></span>
</a>
</section>
@ -28,27 +28,27 @@
<div class="col-md-12">
<div class="col-md-12">
<button type="button" class="btn btn-warning m-t m-b" ng-click="toggleForm()" ng-show="!clientFormVisible" translate>{{ 'add_new_client' | translate }}</button>
<button type="button" class="btn btn-warning m-t m-b" ng-click="toggleForm()" ng-show="!clientFormVisible" translate>{{ 'app.admin.open_api_clients.add_new_client' | translate }}</button>
<form role="form" id="clientForm" ng-show="clientFormVisible" name="clientForm" class="form-inline m-b m-t" novalidate>
<div class="form-group" ng-class="{'has-error': clientForm['client[name]'].$dirty && clientForm['client[name]'].$invalid}">
<input class="form-control" type="text" name="client[name]" ng-model="client.name" value="" placeholder="{{ 'client_name' | translate }}" required>
<input class="form-control" type="text" name="client[name]" ng-model="client.name" value="" placeholder="{{ 'app.admin.open_api_clients.client_name' | translate }}" required>
</div>
<button class="btn btn-default" ng-click="toggleForm()" name="button">{{ 'cancel' | translate }}</button>
<input type="submit" class="btn btn-warning" ng-disabled="!client.name || client.name.length == 0" ng-click="saveClient(client)" value="{{ 'save' | translate }}">
<button class="btn btn-default" ng-click="toggleForm()" name="button">{{ 'app.shared.buttons.cancel' | translate }}</button>
<input type="submit" class="btn btn-warning" ng-disabled="!client.name || client.name.length == 0" ng-click="saveClient(client)" value="{{ 'app.shared.buttons.save' | translate }}">
</form>
<table class="table">
<thead>
<tr>
<th style="width:20%"><a href="" ng-click="setOrder('name')">{{ 'name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': order == 'name', 'fa fa-sort-alpha-desc': order == '-name', 'fa fa-arrows-v': order }"></i></a></th>
<th style="width:20%"><a href="" ng-click="setOrder('name')">{{ 'app.admin.open_api_clients.name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': order == 'name', 'fa fa-sort-alpha-desc': order == '-name', 'fa fa-arrows-v': order }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrder('calls_count')">{{ 'calls_count' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': order == 'calls_count', 'fa fa-sort-numeric-desc': order == '-calls_count', 'fa fa-arrows-v': order }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrder('calls_count')">{{ 'app.admin.open_api_clients.calls_count' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': order == 'calls_count', 'fa fa-sort-numeric-desc': order == '-calls_count', 'fa fa-arrows-v': order }"></i></a></th>
<th style="width:20%"><a href="">{{ 'token' | translate }}</a></th>
<th style="width:20%"><a href="">{{ 'app.admin.open_api_clients.token' | translate }}</a></th>
<th style="width:20%"><a href="" ng-click="setOrder('created_at')">{{ 'created_at' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': order == 'created_at', 'fa fa-sort-numeric-desc': order == '-created_at', 'fa fa-arrows-v': order }"></i></a></th>
<th style="width:20%"><a href="" ng-click="setOrder('created_at')">{{ 'app.admin.open_api_clients.created_at' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': order == 'created_at', 'fa fa-sort-numeric-desc': order == '-created_at', 'fa fa-arrows-v': order }"></i></a></th>
<th style="width:25%"></th>
</tr>
@ -62,11 +62,11 @@
<td>
<div class="buttons">
<button class="btn btn-default" ng-click="editClient(client)">
<i class="fa fa-pencil"></i> {{ 'edit' | translate }}
<i class="fa fa-pencil"></i> {{ 'app.shared.buttons.edit' | translate }}
</button>
<button class="btn btn-default" ng-click="resetToken(client)">
<i class="fa fa-times"></i> {{ 'reset_token' | translate }}
<i class="fa fa-times"></i> {{ 'app.admin.open_api_clients.reset_token' | translate }}
</button>
<button class="btn btn-danger" ng-click="deleteClient($index)" ng-show="client.calls_count == 0">

View File

@ -1,63 +1,63 @@
<h2 translate>{{ 'plan_form.general_information' }}</h2>
<h2 translate>{{ 'app.shared.plan.general_information' }}</h2>
<input type="hidden" name="_method" value="{{method}}">
<div class="form-group" ng-class="{'has-error': planForm['plan[base_name]'].$dirty && planForm['plan[base_name]'].$invalid}">
<label for="plan[base_name]">{{ 'plan_form.name' | translate }} *</label>
<label for="plan[base_name]">{{ 'app.shared.plan.name' | translate }} *</label>
<input type="text" id="plan[base_name]"
name="plan[base_name]"
class="form-control"
ng-maxlength="24"
ng-model="plan.base_name"
required="required"/>
<span class="help-block error" ng-show="planForm['plan[base_name]'].$dirty && planForm['plan[base_name]'].$error.required" translate>{{ 'plan_form.name_is_required' }}</span>
<span class="help-block error" ng-show="planForm['plan[base_name]'].$dirty && planForm['plan[base_name]'].$error.maxlength" translate>{{ 'plan_form.name_length_must_be_less_than_24_characters' }}</span>
<span class="help-block error" ng-show="planForm['plan[base_name]'].$dirty && planForm['plan[base_name]'].$error.required" translate>{{ 'app.shared.plan.name_is_required' }}</span>
<span class="help-block error" ng-show="planForm['plan[base_name]'].$dirty && planForm['plan[base_name]'].$error.maxlength" translate>{{ 'app.shared.plan.name_length_must_be_less_than_24_characters' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': planForm['plan[type]'].$dirty && planForm['plan[type]'].$invalid}">
<label for="plan[type]">{{ 'plan_form.type' | translate }} *</label>
<label for="plan[type]">{{ 'app.shared.plan.type' | translate }} *</label>
<select id="plan[type]"
name="plan[type]"
class="form-control"
ng-model="plan.type"
required="required"
ng-disabled="method == 'PATCH'">
<option value="Plan" ng-selected="plan.type == 'Plan'" translate>{{ 'standard' }}</option>
<option value="PartnerPlan" ng-selected="plan.type == 'PartnerPlan'" translate>{{ 'partner' }}</option>
<option value="Plan" ng-selected="plan.type == 'Plan'" translate>{{ 'app.shared.plan.standard' }}</option>
<option value="PartnerPlan" ng-selected="plan.type == 'PartnerPlan'" translate>{{ 'app.shared.plan.partner' }}</option>
</select>
<span class="help-block error" ng-show="planForm['plan[type]'].$dirty && planForm['plan[type]'].$error.required" translate>{{ 'plan_form.type_is_required' }}</span>
<span class="help-block error" ng-show="planForm['plan[type]'].$dirty && planForm['plan[type]'].$error.required" translate>{{ 'app.shared.plan.type_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': planForm['plan[group_id]'].$dirty && planForm['plan[group_id]'].$invalid}">
<label for="plan[group_id]">{{ 'plan_form.group' | translate }} *</label>
<label for="plan[group_id]">{{ 'app.shared.plan.group' | translate }} *</label>
<select id="plan[group_id]"
name="plan[group_id]"
class="form-control"
ng-model="plan.group_id"
required="required"
ng-disabled="method == 'PATCH'">
<option value="all" translate>{{ 'plan_form.transversal_all_groups' }}</option>
<option value="all" translate>{{ 'app.shared.plan.transversal_all_groups' }}</option>
<optgroup label="Groupes">
<option ng-repeat="group in groups" value="{{group.id}}" ng-selected="plan.group_id == group.id">{{group.name}}</option>
</optgroup>
</select>
<span class="help-block" ng-show="planForm['plan[group_id]'].$dirty && planForm['plan[group_id]'].$error.required" translate>{{ 'plan_form.group_is_required' }}</span>
<span class="help-block" ng-show="planForm['plan[group_id]'].$dirty && planForm['plan[group_id]'].$error.required" translate>{{ 'app.shared.plan.group_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': planForm['plan[interval]'].$dirty && planForm['plan[interval]'].$invalid}">
<label for="plan[interval]">{{ 'plan_form.period' | translate }} *</label>
<label for="plan[interval]">{{ 'app.shared.plan.period' | translate }} *</label>
<select id="plan[interval]"
name="plan[interval]"
class="form-control"
ng-model="plan.interval"
ng-disabled="method == 'PATCH'"
required="required">
<option value="week" ng-selected="plan.interval == 'week'" translate>{{ 'plan_form.week' }}</option>
<option value="month" ng-selected="plan.interval == 'month'" translate>{{ 'plan_form.month' }}</option>
<option value="year" ng-selected="plan.interval == 'year'" translate>{{ 'plan_form.year' }}</option>
<option value="week" ng-selected="plan.interval == 'week'" translate>{{ 'app.shared.plan.week' }}</option>
<option value="month" ng-selected="plan.interval == 'month'" translate>{{ 'app.shared.plan.month' }}</option>
<option value="year" ng-selected="plan.interval == 'year'" translate>{{ 'app.shared.plan.year' }}</option>
</select>
<span class="help-block" ng-show="planForm['plan[interval]'].$dirty && planForm['plan[interval]'].$error.required" translate>{{ 'plan_form.period_is_required' }}</span>
<span class="help-block" ng-show="planForm['plan[interval]'].$dirty && planForm['plan[interval]'].$error.required" translate>{{ 'app.shared.plan.period_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': planForm['plan[interval_count]'].$dirty && planForm['plan[interval_count]'].$invalid}">
<label for="plan[interval]">{{ 'plan_form.number_of_periods' | translate }} *</label>
<label for="plan[interval]">{{ 'app.shared.plan.number_of_periods' | translate }} *</label>
<input id="plan[interval_count]"
name="plan[interval_count]"
class="form-control"
@ -66,12 +66,12 @@
ng-disabled="method == 'PATCH'"
required="required"
min="1"/>
<span class="help-block" ng-show="planForm['plan[interval_count]'].$dirty && planForm['plan[interval_count]'].$error.required" translate>{{ 'plan_form.number_of_periods_is_required' }}</span>
<span class="help-block" ng-show="planForm['plan[interval_count]'].$dirty && planForm['plan[interval_count]'].$error.required" translate>{{ 'app.shared.plan.number_of_periods_is_required' }}</span>
</div>
<div class="form-group">
<div class="input-group" ng-class="{'has-error': planForm['plan[amount]'].$dirty && planForm['plan[amount]'].$invalid}">
<label for="plan[amount]">{{ 'plan_form.subscription_price' | translate }} *</label>
<label for="plan[amount]">{{ 'app.shared.plan.subscription_price' | translate }} *</label>
<div class="input-group">
<span class="input-group-addon">{{currencySymbol}}</span>
<input id="plan[amount]"
@ -81,40 +81,40 @@
ng-required="true"
ng-model="plan.amount"/>
</div>
<span class="help-block" ng-show="planForm['plan[amount]'].$dirty && planForm['plan[amount]'].$error.required" translate>{{ 'plan_form.price_is_required' }}</span>
<span class="help-block" ng-show="planForm['plan[amount]'].$dirty && planForm['plan[amount]'].$error.required" translate>{{ 'app.shared.plan.price_is_required' }}</span>
</div>
</div>
<div class="form-group">
<label translate>{{ 'plan_form.visual_prominence_of_the_subscription' }}</label>
<label translate>{{ 'app.shared.plan.visual_prominence_of_the_subscription' }}</label>
<input ng-model="plan.ui_weight"
type="number"
name="plan[ui_weight]"
class="form-control">
<span class="help-block">
{{ 'plan_form.on_the_subscriptions_page_the_most_prominent_subscriptions_will_be_placed_at_the_top_of_the_list' | translate }}
{{ 'plan_form.an_evelated_number_means_a_higher_prominence' | translate }}
{{ 'app.shared.plan.on_the_subscriptions_page_the_most_prominent_subscriptions_will_be_placed_at_the_top_of_the_list' | translate }}
{{ 'app.shared.plan.an_evelated_number_means_a_higher_prominence' | translate }}
</span>
</div>
<div class="input-group m-t-md">
<label for="plan[is_rolling]" class="control-label m-r-md">{{ 'plan_form.rolling_subscription' | translate }} *</label>
<label for="plan[is_rolling]" class="control-label m-r-md">{{ 'app.shared.plan.rolling_subscription' | translate }} *</label>
<input bs-switch
ng-model="plan.is_rolling"
id="plan[is_rolling]"
ng-if="method != 'PATCH'"
type="checkbox"
class="form-control"
switch-on-text="{{ 'yes' | translate }}"
switch-off-text="{{ 'no' | translate }}"
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true"
ng-true-value="'true'"
ng-false-value="'false'"/>
<span ng-if="method == 'PATCH'">{{ (plan.is_rolling ? 'yes' : 'no') | translate }}</span>
<input type="hidden" name="plan[is_rolling]" value="{{plan.is_rolling}}"/>
<span class="help-block">
{{ 'plan_form.a_rolling_subscription_will_begin_the_day_of_the_first_training' | translate }}
{{ 'plan_form.otherwise_it_will_begin_as_soon_as_it_is_bought' | translate }}
{{ 'app.shared.plan.a_rolling_subscription_will_begin_the_day_of_the_first_training' | translate }}
{{ 'app.shared.plan.otherwise_it_will_begin_as_soon_as_it_is_bought' | translate }}
</span>
</div>
@ -122,13 +122,13 @@
<!-- PDF description attachement -->
<input type="hidden" ng-model="plan.plan_file_attributes.id" name="plan[plan_file_attributes][id]" ng-value="plan.plan_file_attributes.id" />
<input type="hidden" ng-model="plan.plan_file_attributes._destroy" name="plan[plan_file_attributes][_destroy]" ng-value="plan.plan_file_attributes._destroy"/>
<label class="m-t-md" translate>{{ 'plan_form.information_sheet' }}</label>
<label class="m-t-md" translate>{{ 'app.shared.plan.information_sheet' }}</label>
<div class="fileinput input-group" data-provides="fileinput" ng-class="fileinputClass(plan.plan_file_attributes)">
<div class="form-control" data-trigger="fileinput">
<i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">{{file.attachment || plan.plan_file_attributes.attachment_identifier}}</span>
</div>
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new" translate>{{ 'plan_form.attach_an_information_sheet' }}</span>
<span class="fileinput-exists" translate>{{ 'change' }}</span><input type="file"
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new" translate>{{ 'app.shared.plan.attach_an_information_sheet' }}</span>
<span class="fileinput-exists" translate>{{ 'app.shared.buttons.change' }}</span><input type="file"
name="plan[plan_file_attributes][attachment]"
accept="image/*, application/pdf"></span>
<a class="input-group-addon btn btn-danger fileinput-exists" data-dismiss="fileinput" ng-click="deleteFile(file || plan.plan_file_attributes)"><i class="fa fa-trash-o"></i></a>
@ -136,7 +136,7 @@
<div class="form-group m-t-md" ng-show="plan.type == 'PartnerPlan' && method != 'PATCH'">
<input type="hidden" ng-model="plan.partnerId" name="plan[partner_id]" ng-value="plan.partnerId" />
<label for="plan[partner_id]">{{ 'plan_form.notified_partner' | translate }} *</label>
<label for="plan[partner_id]">{{ 'app.shared.plan.notified_partner' | translate }} *</label>
<div class="input-group">
<select class="form-control"
ng-model="plan.partnerId"
@ -145,10 +145,10 @@
<option value=""></option>
</select>
<span class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="openPartnerNewModal()"><i class="fa fa-user-plus"></i> {{ 'plan_form.new_user' | translate }}</button>
<button class="btn btn-default" type="button" ng-click="openPartnerNewModal()"><i class="fa fa-user-plus"></i> {{ 'app.shared.plan.new_user' | translate }}</button>
</span>
</div>
<span class="help-block" translate>{{ 'plan_form.as_part_of_a_partner_subscription_some_notifications_may_be_sent_to_this_user' }}</span>
<span class="help-block" translate>{{ 'app.shared.plan.as_part_of_a_partner_subscription_some_notifications_may_be_sent_to_this_user' }}</span>
</div>
<div class="form-group" ng-show="plan.partners">

View File

@ -7,13 +7,13 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1>{{ 'edit_plan.subscription_plan' | translate }} {{ plan.base_name }}</h1>
<h1>{{ 'app.admin.plans.edit.subscription_plan' | translate }} {{ plan.base_name }}</h1>
</section>
</div>
<div class="col-md-3">
<section class="heading-actions wrapper">
<a class="btn btn-lg btn-block btn-default m-t-xs" ui-sref="app.admin.pricing" translate>{{ 'cancel' }}</a>
<a class="btn btn-lg btn-block btn-default m-t-xs" ui-sref="app.admin.pricing" translate>{{ 'app.shared.buttons.cancel' }}</a>
</section>
</div>
@ -28,37 +28,37 @@
<div id="planForm">
<form name="planForm" novalidate="novalidate" class="col-lg-7 col-lg-offset-2 m-t-lg form-group" action="{{ actionUrl }}" ng-upload="afterSubmit(content)" upload-options-enable-rails-csrf="true">
<ng-include src="'<%= asset_path 'admin/plans/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/plans/_form.html" %>'"></ng-include>
<div class="input-group m-t-md">
<label for="plan[disabled]" class="control-label m-r-md">{{ 'plan_form.disabled' | translate }}</label>
<label for="plan[disabled]" class="control-label m-r-md">{{ 'app.shared.plans.disabled' | translate }}</label>
<input bs-switch
ng-model="plan.disabled"
id="plan[disabled]"
type="checkbox"
class="form-control"
switch-on-text="{{ 'yes' | translate }}"
switch-off-text="{{ 'no' | translate }}"
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true"
ng-true-value="'true'"
ng-false-value="'false'"/>
<input type="hidden" name="plan[disabled]" value="{{plan.disabled}}"/>
<span class="help-block" translate>{{ 'plan_form.disable_plan_will_not_unsubscribe_users' }}</span>
<span class="help-block" translate>{{ 'app.shared.plans.disable_plan_will_not_unsubscribe_users' }}</span>
</div>
<h2 class="m-t-xl" translate>{{ 'edit_plan.prices' }}</h2>
<h2 class="m-t-xl" translate>{{ 'app.admin.plans.edit.prices' }}</h2>
<div class="form-group col-md-6 col-lg-offset-6">
<input type="hidden" ng-model="plan.parent" name="plan[parent_id]" ng-value="plan.parent"/>
<label for="parentPlan" translate>{{ 'edit_plan.copy_prices_from' }}</label>
<label for="parentPlan" translate>{{ 'app.admin.plans.edit.copy_prices_from' }}</label>
<select id="parentPlan" ng-options="plan.id as humanReadablePlanName(plan, groups) for plan in plans" ng-model="plan.parent" ng-change="copyPricesFromPlan()" class="form-control">
<option value=""></option>
</select>
</div>
<h3 translate>{{ 'edit_plan.machines' }}</h3>
<h3 translate>{{ 'app.admin.plans.edit.machines' }}</h3>
<table class="table">
<thead>
<th translate>{{ 'edit_plan.machine' }}</th>
<th translate>{{ 'edit_plan.hourly_rate' }}</th>
<th translate>{{ 'app.admin.plans.edit.machine' }}</th>
<th translate>{{ 'app.admin.plans.edit.hourly_rate' }}</th>
</thead>
<tbody>
@ -75,11 +75,11 @@
</tbody>
</table>
<h3 ng-hide="fablabWithoutSpaces" translate>{{ 'edit_plan.spaces' }}</h3>
<h3 ng-hide="fablabWithoutSpaces" translate>{{ 'app.admin.plans.edit.spaces' }}</h3>
<table class="table" ng-hide="fablabWithoutSpaces">
<thead>
<th translate>{{ 'edit_plan.space' }}</th>
<th translate>{{ 'edit_plan.hourly_rate' }}</th>
<th translate>{{ 'app.admin.plans.edit.space' }}</th>
<th translate>{{ 'app.admin.plans.edit.hourly_rate' }}</th>
</thead>
<tbody>
@ -97,7 +97,7 @@
</table>
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="planForm.$invalid"/>
<input type="submit" value="{{ 'app.shared.buttons.confirm_changes' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="planForm.$invalid"/>
</div>
</form>
</div>

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'new_plan.add_a_subscription_plan' }}</h1>
<h1 translate>{{ 'app.admin.plans.new.add_a_subscription_plan' }}</h1>
</section>
</div>
@ -20,10 +20,10 @@
<div id="planForm">
<form name="planForm" novalidate="novalidate" class="col-lg-10 col-lg-offset-2 m-t-lg form-group" action="{{ actionUrl }}" ng-upload="afterSubmit(content)" upload-options-enable-rails-csrf="true">
<ng-include src="'<%= asset_path 'admin/plans/_form.html' %>'"></ng-include>
<ng-include src="'<%= asset_path "admin/plans/_form.html" %>'"></ng-include>
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="planForm.$invalid || !partnerIsValid()"/>
<input type="submit" value="{{ 'app.shared.buttons.save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="planForm.$invalid || !partnerIsValid()"/>
</div>
</form>

View File

@ -1,15 +1,15 @@
<h2 translate>{{ 'pricing.list_of_the_coupons' }}</h2>
<h2 translate>{{ 'app.admin.pricing.list_of_the_coupons' }}</h2>
<div class="m-t-lg m-b">
<button type="button" class="btn btn-warning" ui-sref="app.admin.coupons_new">
<i class="fa fa-plus m-r"></i>
<span translate>{{ 'pricing.add_a_new_coupon' }}</span>
<span translate>{{ 'app.admin.pricing.add_a_new_coupon' }}</span>
</button>
<div class="form-group pull-right">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<select ng-model="filter.coupon" class="form-control" ng-change="updateCouponFilter()">
<option ng-repeat="status in couponStatus" value="{{status}}" translate>{{ 'pricing.'+status }}</option>
<option ng-repeat="status in couponStatus" value="{{status}}" translate>{{ 'app.admin.pricing.'+status }}</option>
</select>
</div>
</div>
@ -18,10 +18,10 @@
<table class="table">
<thead>
<tr>
<th translate>{{ 'pricing.name' }}</th>
<th translate>{{ 'pricing.discount' }}</th>
<th translate>{{ 'pricing.nb_of_usages' }}</th>
<th translate>{{ 'pricing.status' }}</th>
<th translate>{{ 'app.admin.pricing.name' }}</th>
<th translate>{{ 'app.admin.pricing.discount' }}</th>
<th translate>{{ 'app.admin.pricing.nb_of_usages' }}</th>
<th translate>{{ 'app.admin.pricing.status' }}</th>
<th></th>
</tr>
</thead>
@ -33,7 +33,7 @@
<span ng-show="coupon.type == 'amount_off'">{{coupon.amount_off}} {{currencySymbol}}</span>
</td>
<td>{{coupon.usages}}</td>
<td translate>{{'pricing.'+coupon.status}}</td>
<td translate>{{'app.admin.pricing.'+coupon.status}}</td>
<td>
<button type="button" class="btn btn-default" ng-click="sendCouponToUser(coupon)"><i class="fa fa-send-o"></i> </button>
<button type="button" class="btn btn-default" ui-sref="app.admin.coupons_edit({id:coupon.id})"><i class="fa fa-pencil-square-o"></i></button>
@ -44,5 +44,5 @@
</table>
<div class="text-center">
<button class="btn btn-warning" ng-click="loadMore()" ng-hide="coupons.length === 0 || coupons.length >= coupons[0].total"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'pricing.display_more_coupons' | translate }}</button>
<button class="btn btn-warning" ng-click="loadMore()" ng-hide="coupons.length === 0 || coupons.length >= coupons[0].total"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'app.admin.pricing.display_more_coupons' | translate }}</button>
</div>

View File

@ -1,10 +1,10 @@
<h2 class="m-t-lg" translate>{{ 'pricing.trainings' }}</h2>
<h2 class="m-t-lg" translate>{{ 'app.admin.pricing.trainings' }}</h2>
<table class="table">
<thead>
<tr>
<th style="width:20%" translate>{{ 'pricing.subscription' }}</th>
<th style="width:10%" translate>{{ 'pricing.credits' }}</th>
<th style="width:50%" translate>{{ 'pricing.related_trainings' }}</th>
<th style="width:20%" translate>{{ 'app.admin.pricing.subscription' }}</th>
<th style="width:10%" translate>{{ 'app.admin.pricing.credits' }}</th>
<th style="width:50%" translate>{{ 'app.admin.pricing.related_trainings' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -35,7 +35,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> {{ 'edit' | translate }}
<i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}
</button>
</div>
</td>
@ -43,17 +43,17 @@
</tbody>
</table>
<h2 class="m-t-lg" translate>{{ 'pricing.machines' }}</h2>
<h2 class="m-t-lg" translate>{{ 'app.admin.pricing.machines' }}</h2>
<div class="btn-group m-t-md m-b-md">
<button type="button" class="btn btn-warning" ng-click="addMachineCredit($event)" translate>{{ 'pricing.add_a_machine_credit' }}</button>
<button type="button" class="btn btn-warning" ng-click="addMachineCredit($event)" translate>{{ 'app.admin.pricing.add_a_machine_credit' }}</button>
</div>
<table class="table">
<thead>
<tr>
<th style="width:20%" translate>{{ 'pricing.machine' }}</th>
<th style="width:10%" translate>{{ 'pricing.hours' }}</th>
<th style="width:50%" translate>{{ 'pricing.related_subscriptions' }}</th>
<th style="width:20%" translate>{{ 'app.admin.pricing.machine' }}</th>
<th style="width:10%">{{ 'app.admin.pricing.hours' | translate:{DURATION:slotDuration} }}</th>
<th style="width:50%" translate>{{ 'app.admin.pricing.related_subscriptions' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -85,10 +85,10 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> {{ 'edit' | translate }}
<i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}
</button>
<button class="btn btn-danger" ng-click="removeMachineCredit($index)">
<i class="fa fa-trash-o"></i> {{ 'delete' | translate }} (!)
<i class="fa fa-trash-o"></i> {{ 'app.shared.buttons.delete' | translate }}
</button>
</div>
</td>
@ -96,16 +96,16 @@
</tbody>
</table>
<h2 ng-hide="fablabWithoutSpaces" class="m-t-lg" translate>{{ 'pricing.spaces' }}</h2>
<h2 ng-hide="fablabWithoutSpaces" class="m-t-lg" translate>{{ 'app.admin.pricing.spaces' }}</h2>
<div ng-hide="fablabWithoutSpaces" class="btn-group m-t-md m-b-md">
<button type="button" class="btn btn-warning" ng-click="addSpaceCredit($event)" translate>{{ 'pricing.add_a_space_credit' }}</button>
<button type="button" class="btn btn-warning" ng-click="addSpaceCredit($event)" translate>{{ 'app.admin.pricing.add_a_space_credit' }}</button>
</div>
<table ng-hide="fablabWithoutSpaces" class="table">
<thead>
<tr>
<th style="width:20%" translate>{{ 'pricing.space' }}</th>
<th style="width:10%" translate>{{ 'pricing.hours' }}</th>
<th style="width:50%" translate>{{ 'pricing.related_subscriptions' }}</th>
<th style="width:20%" translate>{{ 'app.admin.pricing.space' }}</th>
<th style="width:10%">{{ 'app.admin.pricing.hours' | translate:{DURATION:slotDuration} }}</th>
<th style="width:50%" translate>{{ 'app.admin.pricing.related_subscriptions' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -137,10 +137,10 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> {{ 'edit' | translate }}
<i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}
</button>
<button class="btn btn-danger" ng-click="removeSpaceCredit($index)">
<i class="fa fa-trash-o"></i> {{ 'delete' | translate }} (!)
<i class="fa fa-trash-o"></i> {{ 'app.shared.buttons.delete' | translate }}
</button>
</div>
</td>

View File

@ -7,7 +7,7 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'pricing.pricing_management' }}</h1>
<h1 translate>{{ 'app.admin.pricing.pricing_management' }}</h1>
</section>
</div>
@ -21,28 +21,28 @@
<div class="col-md-12">
<uib-tabset justified="true">
<uib-tab heading="{{ 'pricing.subscriptions' | translate }}">
<ng-include src="'<%= asset_path 'admin/pricing/subscriptions.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.pricing.subscriptions' | translate }}">
<ng-include src="'<%= asset_path "admin/pricing/subscriptions.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'pricing.trainings' | translate }}">
<ng-include src="'<%= asset_path 'admin/pricing/trainings.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.pricing.trainings' | translate }}">
<ng-include src="'<%= asset_path "admin/pricing/trainings.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'pricing.machine_hours' | translate }}">
<ng-include src="'<%= asset_path 'admin/pricing/machine_hours.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.pricing.machine_hours' | translate }}">
<ng-include src="'<%= asset_path "admin/pricing/machine_hours.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'pricing.spaces' | translate }}" ng-hide="fablabWithoutSpaces">
<ng-include src="'<%= asset_path 'admin/pricing/spaces.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.pricing.spaces' | translate }}" ng-hide="fablabWithoutSpaces">
<ng-include src="'<%= asset_path "admin/pricing/spaces.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'pricing.credits' | translate }}">
<ng-include src="'<%= asset_path 'admin/pricing/credits.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.pricing.credits' | translate }}">
<ng-include src="'<%= asset_path "admin/pricing/credits.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'pricing.coupons' | translate }}">
<ng-include src="'<%= asset_path 'admin/pricing/coupons.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.pricing.coupons' | translate }}">
<ng-include src="'<%= asset_path "admin/pricing/coupons.html" %>'"></ng-include>
</uib-tab>
</uib-tabset>
</div>

View File

@ -1,10 +1,10 @@
<div class="alert alert-warning m-t">
{{ 'pricing.these_prices_match_machine_hours_rates_' | translate }} <span class="font-bold" translate>{{ 'pricing._without_subscriptions' }}</span>.
{{ 'app.admin.pricing.these_prices_match_machine_hours_rates_' | translate:{DURATION:slotDuration} }} <span class="font-bold" translate>{{ 'app.admin.pricing._without_subscriptions' }}</span>.
</div>
<table class="table">
<thead>
<tr>
<th style="width:20%" translate>{{ 'pricing.machines' }}</th>
<th style="width:20%" translate>{{ 'app.admin.pricing.machines' }}</th>
<th style="width:20%" ng-repeat="group in enabledGroups">
<span class="text-u-c text-sm">{{group.name}}</span>
</th>

View File

@ -1,11 +1,11 @@
<div class="modal-header">
<h3 class="text-center red" translate>{{ 'pricing.send_a_coupon' }}</h3>
<h3 class="text-center red" translate>{{ 'app.admin.pricing.send_a_coupon' }}</h3>
</div>
<div class="modal-body">
<select-member></select-member>
<div class="widget panel b-a m">
<div class="panel-heading b-b small">
<h3 class="panel-title" translate>{{ 'pricing.coupon' }}</h3>
<h3 class="panel-title" translate>{{ 'app.admin.pricing.coupon' }}</h3>
</div>
<div class="widget-content no-bg auto wrapper">
<table>
@ -13,18 +13,18 @@
<tr><th style="width:60%"></th></tr>
</thead>
<tbody>
<tr><td translate>{{'pricing.code'}}</td> <td>{{coupon.code}}</td></tr>
<tr><td translate>{{'pricing.discount'}}</td> <td><span ng-show="coupon.type == 'percent_off'">{{coupon.percent_off}} %</span><span ng-show="coupon.type == 'amount_off'">{{coupon.amount_off}} {{currencySymbol}}</span></td></tr>
<tr><td translate>{{'pricing.validity_per_user'}}</td> <td translate>{{'pricing.'+coupon.validity_per_user}}</td></tr>
<tr><td translate>{{'pricing.valid_until'}}</td> <td>{{coupon.valid_until | amDateFormat:'L'}}</td></tr>
<tr><td translate>{{'pricing.usages'}}</td> <td>{{coupon.usages}} / {{coupon.max_usages | maxCount}}</td></tr>
<tr><td translate>{{'pricing.enabled'}}</td> <td>{{coupon.active | booleanFormat}}</td></tr>
<tr><td translate>{{'app.admin.pricing.code'}}</td> <td>{{coupon.code}}</td></tr>
<tr><td translate>{{'app.admin.pricing.discount'}}</td> <td><span ng-show="coupon.type == 'percent_off'">{{coupon.percent_off}} %</span><span ng-show="coupon.type == 'amount_off'">{{coupon.amount_off}} {{currencySymbol}}</span></td></tr>
<tr><td translate>{{'app.admin.pricing.validity_per_user'}}</td> <td translate>{{'app.admin.pricing.'+coupon.validity_per_user}}</td></tr>
<tr><td translate>{{'app.admin.pricing.valid_until'}}</td> <td>{{coupon.valid_until | amDateFormat:'L'}}</td></tr>
<tr><td translate>{{'app.admin.pricing.usages'}}</td> <td>{{coupon.usages}} / {{coupon.max_usages | maxCount}}</td></tr>
<tr><td translate>{{'app.admin.pricing.enabled'}}</td> <td>{{coupon.active | booleanFormat}}</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" ng-disabled="ctrl.member == null" translate>{{ 'confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button>
<button class="btn btn-warning" ng-click="ok()" ng-disabled="ctrl.member == null" translate>{{ 'app.shared.buttons.confirm' }}</button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -1,10 +1,10 @@
<div class="alert alert-warning m-t">
{{ 'pricing.these_prices_match_space_hours_rates_' | translate }} <span class="font-bold" translate>{{ 'pricing._without_subscriptions' }}</span>.
{{ 'app.admin.pricing.these_prices_match_space_hours_rates_' | translate:{DURATION:slotDuration} }} <span class="font-bold" translate>{{ 'app.admin.pricing._without_subscriptions' }}</span>.
</div>
<table class="table">
<thead>
<tr>
<th style="width:20%" translate>{{ 'pricing.spaces' }}</th>
<th style="width:20%" translate>{{ 'app.admin.pricing.spaces' }}</th>
<th style="width:20%" ng-repeat="group in enabledGroups">
<span class="text-u-c text-sm">{{group.name}}</span>
</th>

View File

@ -1,21 +1,21 @@
<h2 translate>{{ 'pricing.list_of_the_subscription_plans' }}</h2>
<h2 translate>{{ 'app.admin.pricing.list_of_the_subscription_plans' }}</h2>
<div ng-show="fablabWithoutPlans" class="alert alert-warning m-t">
{{ 'pricing.beware_the_subscriptions_are_disabled_on_this_application' | translate }}
{{ 'pricing.you_can_create_some_but_they_wont_be_available_until_the_project_is_redeployed_by_the_server_manager' | translate }}
<br>{{ 'pricing.for_safety_reasons_please_dont_create_subscriptions_if_you_dont_want_intend_to_use_them_later' | translate }}
{{ 'app.admin.pricing.beware_the_subscriptions_are_disabled_on_this_application' | translate }}
{{ 'app.admin.pricing.you_can_create_some_but_they_wont_be_available_until_the_project_is_redeployed_by_the_server_manager' | translate }}
<br>{{ 'app.admin.pricing.for_safety_reasons_please_dont_create_subscriptions_if_you_dont_want_intend_to_use_them_later' | translate }}
</div>
<div class="m-t-lg">
<button type="button" class="btn btn-warning" ui-sref="app.admin.plans.new">
<i class="fa fa-plus m-r"></i>
<span translate>{{ 'pricing.add_a_new_subscription_plan' }}</span>
<span translate>{{ 'app.admin.pricing.add_a_new_subscription_plan' }}</span>
</button>
<div class="form-group pull-right">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<select ng-model="planFiltering" class="form-control">
<option ng-repeat="status in filterDisabled" value="{{status}}" translate>{{ 'pricing.status_'+status }}</option>
<option ng-repeat="status in filterDisabled" value="{{status}}" translate>{{ 'app.admin.pricing.status_'+status }}</option>
</select>
</div>
</div>
@ -24,12 +24,12 @@
<table class="table">
<thead>
<tr>
<th><a href="" ng-click="setOrderPlans('type')">{{ 'pricing.type' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderPlans=='type', 'fa fa-sort-alpha-desc': orderPlans=='-type', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('name')">{{ 'pricing.name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderPlans=='name', 'fa fa-sort-alpha-desc': orderPlans=='-name', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('interval')">{{ 'pricing.duration' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-amount-asc': orderPlans=='interval', 'fa fa-sort-amount-desc': orderPlans=='-interval', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('group_id')">{{ 'pricing.group' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderPlans=='group_id', 'fa fa-sort-alpha-desc': orderPlans=='-group_id', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th class="hidden-xs"><a href="" ng-click="setOrderPlans('pricing.ui_weight')">{{ 'pricing.prominence' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderPlans=='ui_weight', 'fa fa-sort-numeric-desc': orderPlans=='-ui_weight', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('amount')">{{ 'pricing.price' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderPlans=='amount', 'fa fa-sort-numeric-desc': orderPlans=='-amount', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('type')">{{ 'app.admin.pricing.type' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderPlans=='type', 'fa fa-sort-alpha-desc': orderPlans=='-type', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('name')">{{ 'app.admin.pricing.name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderPlans=='name', 'fa fa-sort-alpha-desc': orderPlans=='-name', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('interval')">{{ 'app.admin.pricing.duration' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-amount-asc': orderPlans=='interval', 'fa fa-sort-amount-desc': orderPlans=='-interval', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('group_id')">{{ 'app.admin.pricing.group' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderPlans=='group_id', 'fa fa-sort-alpha-desc': orderPlans=='-group_id', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th class="hidden-xs"><a href="" ng-click="setOrderPlans('app.admin.pricing.ui_weight')">{{ 'app.admin.pricing.prominence' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderPlans=='ui_weight', 'fa fa-sort-numeric-desc': orderPlans=='-ui_weight', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th><a href="" ng-click="setOrderPlans('amount')">{{ 'app.admin.pricing.price' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderPlans=='amount', 'fa fa-sort-numeric-desc': orderPlans=='-amount', 'fa fa-arrows-v': orderPlans }"></i></a></th>
<th></th>
</tr>
</thead>

View File

@ -1,7 +1,7 @@
<table class="table">
<thead>
<tr>
<th style="width:20%" translate>{{ 'pricing.trainings' }}</th>
<th style="width:20%" translate>{{ 'app.admin.pricing.trainings' }}</th>
<th style="width:20%" ng-repeat="group in enabledGroups">
<span class="text-u-c text-sm">{{group.name}}</span>
</th>

View File

@ -7,12 +7,12 @@
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'project_elements.projects_elements_management' }}</h1>
<h1 translate>{{ 'app.admin.project_elements.projects_elements_management' }}</h1>
</section>
</div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
<section class="heading-actions wrapper">
<a class="btn btn-ng btn-warning b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.manage_abuses" role="button" translate>{{ 'project_elements.manage_abuses' }}</a>
<a class="btn btn-ng btn-warning b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.manage_abuses" role="button" translate>{{ 'app.admin.project_elements.manage_abuses' }}</a>
</section>
</div>
</div>
@ -24,14 +24,14 @@
<div class="col-md-12">
<uib-tabset justified="true">
<uib-tab heading="{{ 'materials' | translate }}">
<ng-include src="'<%= asset_path 'admin/project_elements/materials.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.project_elements.materials' | translate }}">
<ng-include src="'<%= asset_path "admin/project_elements/materials.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'themes' | translate }}">
<ng-include src="'<%= asset_path 'admin/project_elements/themes.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.project_elements.themes' | translate }}">
<ng-include src="'<%= asset_path "admin/project_elements/themes.html" %>'"></ng-include>
</uib-tab>
<uib-tab heading="{{ 'project_elements.licences' | translate }}">
<ng-include src="'<%= asset_path 'admin/project_elements/licences.html' %>'"></ng-include>
<uib-tab heading="{{ 'app.admin.project_elements.licences' | translate }}">
<ng-include src="'<%= asset_path "admin/project_elements/licences.html" %>'"></ng-include>
</uib-tab>
</uib-tabset>
</div>

View File

@ -1,10 +1,10 @@
<button type="button" class="btn btn-warning m-t m-b" ng-click="addLicence()" translate>{{ 'project_elements.add_a_new_licence' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ng-click="addLicence()" translate>{{ 'app.admin.project_elements.add_a_new_licence' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:30%" translate>{{ 'name' }}</th>
<th style="width:50%" class="hidden-xs" translate>{{ 'description' }}</th>
<th style="width:30%" translate>{{ 'app.admin.project_elements.name' }}</th>
<th style="width:50%" class="hidden-xs" translate>{{ 'app.admin.project_elements.description' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -32,7 +32,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removeLicence($index)">
<i class="fa fa-trash-o"></i>

View File

@ -1,9 +1,9 @@
<button type="button" class="btn btn-warning m-b m-t" ng-click="addComponent()" translate>{{ 'project_elements.add_a_material' }}</button>
<button type="button" class="btn btn-warning m-b m-t" ng-click="addComponent()" translate>{{ 'app.admin.project_elements.add_a_material' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:80%" translate>{{ 'name' }}</th>
<th style="width:80%" translate>{{ 'app.admin.project_elements.name' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -26,7 +26,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removeComponent($index)">
<i class="fa fa-trash-o"></i>

View File

@ -1,9 +1,9 @@
<button type="button" class="btn btn-warning m-t m-b" ng-click="addTheme()" translate>{{ 'project_elements.add_a_new_theme' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ng-click="addTheme()" translate>{{ 'app.admin.project_elements.add_a_new_theme' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:80%" translate>{{ 'name' }}</th>
<th style="width:80%" translate>{{ 'app.admin.project_elements.name' }}</th>
<th style="width:20%"></th>
</tr>
</thead>
@ -26,7 +26,7 @@
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()">
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'edit' }}</span>
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm" translate>{{ 'app.shared.buttons.edit' }}</span>
</button>
<button class="btn btn-danger" ng-click="removeTheme($index)">
<i class="fa fa-trash-o"></i>

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