1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +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: To report a bug, please describe:
- Expected behavior and actual behavior. - Expected behavior and actual behavior.
- Steps to reproduce the problem. - Steps to reproduce the problem.
- Specifications like the version of the project, operating system, or hardware. - 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 # 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 ## 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) - 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' gem 'webmock'
end end
group :production do group :production, :staging do
gem 'rails_12factor' gem 'rails_12factor'
end end
@ -152,3 +152,5 @@ gem 'sys-filesystem'
gem 'sha3' gem 'sha3'
gem 'repost' gem 'repost'
gem 'icalendar'

View File

@ -191,6 +191,9 @@ GEM
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
i18n (0.9.5) i18n (0.9.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
icalendar (2.5.3)
ice_cube (~> 0.16)
ice_cube (0.16.3)
ice_nine (0.11.2) ice_nine (0.11.2)
jaro_winkler (1.5.1) jaro_winkler (1.5.1)
jbuilder (2.5.0) jbuilder (2.5.0)
@ -208,7 +211,7 @@ GEM
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
libv8 (3.16.14.19) libv8 (3.16.14.19)
loofah (2.3.0) loofah (2.3.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.1) mail (2.7.1)
@ -240,7 +243,7 @@ GEM
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.1.1) multipart-post (2.1.1)
naught (1.1.0) naught (1.1.0)
nokogiri (1.10.4) nokogiri (1.10.8)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
notify_with (0.0.2) notify_with (0.0.2)
jbuilder (~> 2.0) jbuilder (~> 2.0)
@ -288,7 +291,7 @@ GEM
pundit (1.0.0) pundit (1.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.1.6) raabro (1.1.6)
rack (1.6.11) rack (1.6.12)
rack-protection (1.5.5) rack-protection (1.5.5)
rack rack
rack-test (0.6.3) rack-test (0.6.3)
@ -497,6 +500,7 @@ DEPENDENCIES
forgery forgery
friendly_id (~> 5.1.0) friendly_id (~> 5.1.0)
has_secure_token has_secure_token
icalendar
jbuilder (~> 2.5) jbuilder (~> 2.5)
jbuilder_cache_multi jbuilder_cache_multi
jquery-rails 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) [![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 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) [![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 ##### Table of Contents
1. [Software stack](#software-stack) 1. [Software stack](#software-stack)
2. [Contributing](#contributing) 2. [Contributing](#contributing)
3. [Setup a production environment](#setup-a-production-environment) 3. [Setup a production environment](#setup-a-production-environment)
4. [Setup a development environment](#setup-a-development-environment)<br/> 4. [Setup a development environment](#setup-a-development-environment)
4.1. [General Guidelines](#general-guidelines)<br/> 5. [Internationalization (i18n)](#i18n)
5. [PostgreSQL](#postgresql)<br/> 6. [Open Projects](#open-projects)
5.1. [Install PostgreSQL 9.6](#setup-postgresql) 7. [Plugins](#plugins)
6. [ElasticSearch](#elasticsearch)<br/> 8. [Single Sign-On](#sso)
6.1. [Install ElasticSearch](#setup-elasticsearch)<br/> 9. [Known issues](#known-issues)
6.2. [Rebuild statistics](#rebuild-stats)<br/> 10. [Related Documentation](#related-documentation)
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)
@ -54,293 +42,22 @@ Contributions are welcome. Please read [the contribution guidelines](CONTRIBUTIN
## Setup a production environment ## 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/). 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> <a name="setup-a-development-environment"></a>
## Setup a development environment ## 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](virtual-machine.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`.
<a name="i18n"></a> <a name="i18n"></a>
## Internationalization (i18n) ## Internationalization (i18n)
The FabManager application can only run in a single language but this language can easily be changed. The FabManager application can only run in a single language but this language can easily be changed.
<a name="i18n-translation"></a> Please refer to the [translation readme](doc/translation_readme.md) for instructions about configuring the language or to contribute to the translation.
### 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.
<a name="open-projects"></a> <a name="open-projects"></a>
## Open Projects ## Open Projects
@ -390,51 +107,7 @@ Developers may find information on how to implement their own authentication pro
<a name="known-issues"></a> <a name="known-issues"></a>
## Known issues ## 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. Before reporting an issue, please check if your issue is not listed in the [know issues](doc/known-issues.md) with its solution.
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.
<a name="related-documentation"></a> <a name="related-documentation"></a>
## Related Documentation ## Related Documentation

View File

@ -59,8 +59,8 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
$translateProvider.useLoaderCache(true); $translateProvider.useLoaderCache(true);
// Secure i18n module against XSS attacks by escaping the output // Secure i18n module against XSS attacks by escaping the output
$translateProvider.useSanitizeValueStrategy('escapeParameters'); $translateProvider.useSanitizeValueStrategy('escapeParameters');
// Enable the MessageFormat interpolation (used for pluralization) // Use the MessageFormat interpolation by default (used for pluralization)
$translateProvider.addInterpolation('$translateMessageFormatInterpolation'); $translateProvider.useMessageFormatInterpolation();
// Set the langage of the instance (from ruby configuration) // Set the langage of the instance (from ruby configuration)
$translateProvider.preferredLanguage(Fablab.locale); $translateProvider.preferredLanguage(Fablab.locale);
}]).run(['$rootScope', '$log', 'AuthService', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics', }]).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; $rootScope.fablabWithoutOnlinePayment = Fablab.withoutOnlinePayment;
// Global config: if true, no invoices will be generated // Global config: if true, no invoices will be generated
$rootScope.fablabWithoutInvoices = Fablab.withoutInvoices; $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). // 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 // 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: { resolve: {
object () { object () {
return { return {
title: _t('manage_abuses.confirmation_required'), title: _t('app.admin.manage_abuses.confirmation_required'),
msg: _t('manage_abuses.report_will_be_destroyed') msg: _t('app.admin.manage_abuses.report_will_be_destroyed')
}; };
} }
} }
}, },
function () { // cancel confirmed function () { // cancel confirmed
Abuse.remove({ id: abuseId }, function () { // successfully canceled 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) { Abuse.query({}, function (abuses) {
$scope.abuses = abuses.abuses.filter(a => a.signaled_type === 'Project'); $scope.abuses = abuses.abuses.filter(a => a.signaled_type === 'Project');
}); });
} }
, function () { // error while canceling , 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.authMethods
* - $scope.mappingFields * - $scope.mappingFields
* - $scope.cancel() * - $scope.cancel()
* - $scope.methodName()
* - $scope.defineDataMapping(mapping) * - $scope.defineDataMapping(mapping)
* *
* Requires : * Requires :
* - mappingFieldsPromise: retrieved by AuthProvider.mapping_fields() * - mappingFieldsPromise: retrieved by AuthProvider.mapping_fields()
* - $state (Ui-Router) [ 'app.admin.members' ] * - $state (Ui-Router) [ 'app.admin.members' ]
* - _t : translation method
*/ */
class AuthenticationController { class AuthenticationController {
constructor ($scope, $state, $uibModal, mappingFieldsPromise) { constructor ($scope, $state, $uibModal, _t, mappingFieldsPromise) {
// list of supported authentication methods // list of supported authentication methods
$scope.authMethods = METHODS; $scope.authMethods = METHODS;
@ -73,6 +75,13 @@ class AuthenticationController {
*/ */
$scope.cancel = function () { $state.go('app.admin.members'); }; $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 * 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); }; $scope.ok = function () { $uibModalInstance.close($scope.transformation.rules); };
// do not save the modifications // 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; }); .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) { $scope.getType = function (type) {
const text = METHODS[type]; const text = METHODS[type];
if (typeof text !== 'undefined') { if (typeof text !== 'undefined') {
return _t(text); return _t(`app.admin.members.authentication_form.${text}`);
} else { } 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) { $scope.getState = function (status) {
switch (status) { switch (status) {
case 'active': return _t('active'); case 'active': return _t('app.admin.members.authentication_form.active');
case 'pending': return _t('pending'); case 'pending': return _t('app.admin.members.authentication_form.pending');
case 'previous': return _t('previous_provider'); case 'previous': return _t('app.admin.members.authentication_form.previous_provider');
default: return _t('unknown') + status; default: return _t('app.admin.members.authentication_form.unknown') + status;
} }
}; };
@ -194,8 +203,8 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.members.authentication_form.confirmation_required'),
msg: _t('do_you_really_want_to_delete_the_TYPE_authentication_provider_NAME', { TYPE: $scope.getType(provider.providable_type), NAME: provider.name }) 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 }, { id: provider.id },
function () { function () {
providers.splice(findIdxById(providers, provider.id), 1); 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 // prevent from adding mode than 1
for (provider of Array.from(authProvidersPromise)) { for (provider of Array.from(authProvidersPromise)) {
if (provider.providable_type === 'DatabaseProvider') { 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 false;
} }
} }
return AuthProvider.save({ auth_provider: $scope.provider }, function (provider) { 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'); return $state.go('app.admin.members');
}); });
// === OAuth2Provider === // === OAuth2Provider ===
} else if ($scope.provider.providable_type === 'OAuth2Provider') { } else if ($scope.provider.providable_type === 'OAuth2Provider') {
// check the ID mapping // check the ID mapping
if (!check_oauth2_id_is_mapped($scope.provider.providable_attributes.o_auth2_mappings_attributes)) { 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; return false;
} }
// discourage the use of unsecure SSO // discourage the use of unsecure SSO
@ -277,24 +286,24 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
resolve: { resolve: {
object () { object () {
return { return {
title: _t('security_issue_detected'), title: _t('app.admin.authentication_new.security_issue_detected'),
msg: _t('beware_the_oauth2_authenticatoin_provider_you_are_about_to_add_isnt_using_HTTPS') + msg: _t('app.admin.authentication_new.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('app.admin.authentication_new.this_is_a_serious_security_issue_on_internet_and_should_never_be_used_except_for_testing_purposes') +
_t('do_you_really_want_to_continue') _t('app.admin.authentication_new.do_you_really_want_to_continue')
}; };
} }
} }
}, },
function () { // unsecured http confirmed function () { // unsecured http confirmed
AuthProvider.save({ auth_provider: $scope.provider }, function (provider) { 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'); return $state.go('app.admin.members');
}); });
} }
); );
} else { } else {
AuthProvider.save({ auth_provider: $scope.provider }, function (provider) { 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'); return $state.go('app.admin.members');
}); });
} }
@ -302,7 +311,7 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
}; };
// Using the AuthenticationController // 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 () { $scope.updateProvider = function () {
// check the ID mapping // check the ID mapping
if (!check_oauth2_id_is_mapped($scope.provider.providable_attributes.o_auth2_mappings_attributes)) { 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 false;
} }
return AuthProvider.update( return AuthProvider.update(
{ id: $scope.provider.id }, { id: $scope.provider.id },
{ auth_provider: $scope.provider }, { auth_provider: $scope.provider },
function (provider) { function (provider) {
growl.success(_t('provider_successfully_updated')); growl.success(_t('app.admin.authentication_edit.provider_successfully_updated'));
$state.go('app.admin.members'); $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 // 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 */ /* PRIVATE STATIC CONSTANTS */
// The calendar is divided in slots of 30 minutes // The calendar is divided in slots of 30 minutes
let loadingCb;
const BASE_SLOT = '00:30:00'; const BASE_SLOT = '00:30:00';
// The bookings can be positioned every half hours // The bookings can be positioned every half hours
const BOOKING_SNAP = '00:30:00'; const BOOKING_SNAP = '00:30:00';
// We do not allow the creation of slots that are not a multiple of 60 minutes // 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 */ /* PUBLIC SCOPE */
@ -40,6 +39,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
// currently selected availability // currently selected availability
$scope.availability = null; $scope.availability = null;
// corresponding fullCalendar item in the DOM
$scope.availabilityDom = null;
// bind the availabilities slots with full-Calendar events // bind the availabilities slots with full-Calendar events
$scope.eventSources = []; $scope.eventSources = [];
$scope.eventSources.push({ $scope.eventSources.push({
@ -62,7 +64,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
return calendarEventClickCb(event, jsEvent, view); return calendarEventClickCb(event, jsEvent, view);
}, },
eventRender (event, element, view) { eventRender (event, element, view) {
return eventRenderCb(event, element); return eventRenderCb(event, element, view);
},
viewRender(view, element) {
return viewRenderCb(view, element);
}, },
loading (isLoading, view) { loading (isLoading, view) {
return loadingCb(isLoading, view); return loadingCb(isLoading, view);
@ -80,10 +85,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
resolve: { resolve: {
object () { object () {
return { return {
title: _t('admin_calendar.confirmation_required'), title: _t('app.admin.calendar.confirmation_required'),
msg: _t('admin_calendar.do_you_really_want_to_cancel_the_USER_s_reservation_the_DATE_at_TIME_concerning_RESERVATION' 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 } , { 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')
}; };
} }
} }
@ -101,10 +105,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
} }
} }
// notify the admin // 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 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) { $scope.removeMachine = function (machine) {
if ($scope.availability.machine_ids.length === 1) { 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 { } else {
// open a confirmation dialog // open a confirmation dialog
return dialogs.confirm({ return dialogs.confirm({
resolve: { resolve: {
object () { object () {
return { return {
title: _t('admin_calendar.confirmation_required'), title: _t('app.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') + ' ' + msg: _t('app.admin.calendar.do_you_really_want_to_remove_MACHINE_from_this_slot', { GENDER: getGender($scope.currentUser), MACHINE: machine.name }) + ' ' +
_t('admin_calendar.this_will_prevent_any_new_reservation_on_this_slot_but_wont_cancel_those_existing') + ' ' + _t('app.admin.calendar.this_will_prevent_any_new_reservation_on_this_slot_but_wont_cancel_those_existing') + '<br><strong>' +
_t('admin_calendar.beware_this_cannot_be_reverted') _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; $scope.availability.title = data.title;
uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents'); uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents');
// notify the admin // 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 , 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) { $scope.alertExport = function (type) {
Export.status({ category: 'availabilities', type }).then(function (res) { Export.status({ category: 'availabilities', type }).then(function (res) {
if (!res.data.exists) { 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: { resolve: {
object () { object () {
return { return {
title: _t('admin_calendar.confirmation_required'), title: _t('app.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') 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 }, { lock: !locked },
function (data) { // success function (data) { // success
$scope.availability = data; $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'); uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
}, },
function (error) { // failed 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); console.error(error);
} }
); );
} }
); );
} else { } 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 () { $scope.removeSlot = function () {
// open a confirmation dialog // open a confirmation dialog
dialogs.confirm( const modalInstance = $uibModal.open({
{ animation: true,
templateUrl: '<%= asset_path "admin/calendar/deleteRecurrent.html" %>',
size: 'md',
controller: 'DeleteRecurrentAvailabilityController',
resolve: { resolve: {
object () { availabilityPromise: ['Availability', function (Availability) { return Availability.get({ id: $scope.availability.id }).$promise; }]
return {
title: _t('admin_calendar.confirmation_required'),
msg: _t('admin_calendar.do_you_really_want_to_delete_this_slot')
};
} }
}
},
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 */ /* PRIVATE SCOPE */
@ -275,10 +271,22 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
var calendarSelectCb = function (start, end, jsEvent, view) { var calendarSelectCb = function (start, end, jsEvent, view) {
start = moment.tz(start.toISOString(), Fablab.timezone); start = moment.tz(start.toISOString(), Fablab.timezone);
end = moment.tz(end.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(); 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 // then we open a modal window to let the admin specify the slot type
const modalInstance = $uibModal.open({ const modalInstance = $uibModal.open({
templateUrl: '<%= asset_path "admin/calendar/eventModal.html" %>', templateUrl: '<%= asset_path "admin/calendar/eventModal.html" %>',
@ -288,7 +296,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
end () { return end; }, end () { return end; },
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }], machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
trainingsPromise: ['Training', function (Training) { return Training.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 // when the modal is closed, we send the slot to the server for saving
modalInstance.result.then( modalInstance.result.then(
@ -312,8 +321,6 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}, },
function () { uiCalendarConfig.calendars.calendar.fullCalendar('unselect'); } function () { uiCalendarConfig.calendars.calendar.fullCalendar('unselect'); }
); );
}
}
return 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) { var calendarEventClickCb = function (event, jsEvent, view) {
$scope.availability = event; $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 the user has clicked on the delete event button, delete the event
if ($(jsEvent.target).hasClass('remove-event')) { if ($(jsEvent.target).hasClass('remove-event')) {
return $scope.removeSlot(); return $scope.removeSlot();
@ -340,7 +353,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* @see http://fullcalendar.io/docs/event_rendering/eventRender/ * @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/ */
var eventRenderCb = function (event, element) { var eventRenderCb = function (event, element) {
if (event.available_type !== 'event') {
element.find('.fc-content').prepend('<span class="remove-event">x&nbsp;</span>'); element.find('.fc-content').prepend('<span class="remove-event">x&nbsp;</span>');
}
if (event.tags.length > 0) { if (event.tags.length > 0) {
let html = ''; let html = '';
for (let tag of Array.from(event.tags)) { for (let tag of Array.from(event.tags)) {
@ -355,12 +370,23 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* Triggered when resource fetching starts/stops. * Triggered when resource fetching starts/stops.
* @see https://fullcalendar.io/docs/resource_data/loading/ * @see https://fullcalendar.io/docs/resource_data/loading/
*/ */
return loadingCb = function (isLoading, view) { const loadingCb = function (isLoading, view) {
if (isLoading) { if (isLoading) {
// we remove existing events when fetching starts to prevent duplicates // 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 * Controller used in the slot creation modal window
*/ */
Application.Controllers.controller('CreateEventModalController', ['$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, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, Tag, growl, _t) { function ($scope, $uibModalInstance, $sce, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, tagsPromise, growl, _t) {
// $uibModal parameter // $uibModal parameter
$scope.start = start; $scope.start = start;
@ -385,6 +411,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
// spaces list // spaces list
$scope.spaces = spacesPromise.filter(function (s) { return !s.disabled; }); $scope.spaces = spacesPromise.filter(function (s) { return !s.disabled; });
// all tags list
$scope.tags = tagsPromise;
// machines associated with the created slot // machines associated with the created slot
$scope.selectedMachines = []; $scope.selectedMachines = [];
@ -416,9 +445,23 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.availability = { $scope.availability = {
start_at: start, start_at: start,
end_at: end, 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 * Adds or removes the provided machine from the current slot
* @param machine {Object} * @param machine {Object}
@ -440,7 +483,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
if ($scope.selectedMachines.length > 0) { if ($scope.selectedMachines.length > 0) {
$scope.availability.machine_ids = $scope.selectedMachines.map(function (m) { return m.id; }); $scope.availability.machine_ids = $scope.selectedMachines.map(function (m) { return m.id; });
} else { } 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; return;
} }
} else if ($scope.availability.available_type === 'training') { } else if ($scope.availability.available_type === 'training') {
@ -448,9 +491,13 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
} else if ($scope.availability.available_type === 'space') { } else if ($scope.availability.available_type === 'space') {
$scope.availability.space_ids = [$scope.selectedSpace.id]; $scope.availability.space_ids = [$scope.selectedSpace.id];
} }
if ($scope.availability.is_recurrent) {
$scope.availability.occurrences = $scope.occurrences;
}
return Availability.save( return Availability.save(
{ availability: $scope.availability } { availability: $scope.availability },
, function (availability) { $uibModalInstance.close(availability); }); function (availability) { $uibModalInstance.close(availability); }
);
}; };
/** /**
@ -458,6 +505,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
*/ */
$scope.next = function () { $scope.next = function () {
if ($scope.step === 1) { $scope.setNbTotalPlaces(); } if ($scope.step === 1) { $scope.setNbTotalPlaces(); }
if ($scope.step === 2) { return validateSelection(); }
if ($scope.step === 4) { return validateRecurrence(); }
return $scope.step++; return $scope.step++;
}; };
@ -495,23 +544,25 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.selectedSpace = $scope.spaces[0]; $scope.selectedSpace = $scope.spaces[0];
} }
Tag.query().$promise.then(function (data) { $scope.tags = data; }); // 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
// 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
// can configure any duration as it does not matters. // can configure any duration as it does not matters.
$scope.$watch('availability.available_type', function (newValue, oldValue, scope) { $scope.$watch('availability.available_type', function (newValue, oldValue, scope) {
if ((newValue === 'machines') || (newValue === 'space')) { if ((newValue === 'machines') || (newValue === 'space')) {
$scope.endDateReadOnly = true; $scope.endDateReadOnly = true;
const diff = moment($scope.end).diff($scope.start, 'hours'); // the result is rounded down by moment.js const slots = Math.trunc(($scope.end.valueOf() - $scope.start.valueOf()) / (60 * 1000)) / Fablab.slotDuration;
$scope.end = moment($scope.start).add(diff, 'hours').toDate(); 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; return $scope.availability.end_at = $scope.end;
} else { } else {
return $scope.endDateReadOnly = false; 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) // maintain the relative length of the slot (ie. change the end time accordingly)
$scope.$watch('start', function (newValue, oldValue, scope) { $scope.$watch('start', function (newValue, oldValue, scope) {
// for machine or space availabilities, adjust the end time // 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'); end.add(moment(newValue).diff(oldValue), 'milliseconds');
$scope.end = end.toDate(); $scope.end = end.toDate();
} else { // for training availabilities } else { // for training availabilities
// prevent the admin from setting the begining after the and // prevent the admin from setting the beginning after the end
if (moment(newValue).add(1, 'hour').isAfter($scope.end)) { if (moment(newValue).add(Fablab.slotDuration, 'minutes').isAfter($scope.end)) {
$scope.start = oldValue; $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 // Maintain consistency between the end time and the date object in the availability object
return $scope.$watch('end', function (newValue, oldValue, scope) { return $scope.$watch('end', function (newValue, oldValue, scope) {
// we prevent the admin from setting the end of the availability before its begining // we prevent the admin from setting the end of the availability before its beginning
if (moment($scope.start).add(1, 'hour').isAfter(newValue)) { if (moment($scope.start).add(Fablab.slotDuration, 'minutes').isAfter(newValue)) {
$scope.end = oldValue; $scope.end = oldValue;
} }
// update availability object // 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 // !!! MUST BE CALLED AT THE END of the controller
return initialize(); 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 */ /* COMMON CODE */
// The validity per user defines how many time a user may ba able to use the same coupon // 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 // Here are the various options for this parameter
const userValidities = ['once', 'forever']; const VALIDITIES = ['once', 'forever'];
/** /**
* Controller used in the coupon creation page * Controller used in the coupon creation page
@ -27,7 +16,7 @@ Application.Controllers.controller('NewCouponController', ['$scope', '$state', '
}; };
// Options for the validity per user // Options for the validity per user
$scope.validities = userValidities; $scope.validities = VALIDITIES;
// Default parameters for AngularUI-Bootstrap datepicker (used for coupon validity limit selection) // Default parameters for AngularUI-Bootstrap datepicker (used for coupon validity limit selection)
$scope.datePicker = { $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 * Shows/hides the validity limit datepicker
* @param $event {Object} jQuery event object * @param $event {Object} jQuery event object
@ -46,17 +42,17 @@ Application.Controllers.controller('NewCouponController', ['$scope', '$state', '
$scope.toggleDatePicker = function ($event) { $scope.toggleDatePicker = function ($event) {
$event.preventDefault(); $event.preventDefault();
$event.stopPropagation(); $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 * 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') Coupon.save({ coupon: $scope.coupon }, coupon => $state.go('app.admin.pricing')
, function (err) { , function (err) {
growl.error(_t('unable_to_create_the_coupon_check_code_already_used')); growl.error(_t('app.admin.coupons_new.unable_to_create_the_coupon_check_code_already_used'));
return console.error(err); console.error(err);
}); });
} }
]); ]);
@ -75,7 +71,7 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
$scope.coupon = couponPromise; $scope.coupon = couponPromise;
// Options for the validity per user // Options for the validity per user
$scope.validities = userValidities; $scope.validities = VALIDITIES;
// Mapping for validation errors // Mapping for validation errors
$scope.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 * Shows/hides the validity limit datepicker
* @param $event {Object} jQuery event object * @param $event {Object} jQuery event object
@ -97,7 +100,7 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
$scope.toggleDatePicker = function ($event) { $scope.toggleDatePicker = function ($event) {
$event.preventDefault(); $event.preventDefault();
$event.stopPropagation(); $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.updateCoupon = function () {
$scope.errors = {}; $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) { , function (err) {
growl.error(_t('unable_to_update_the_coupon_an_error_occurred')); growl.error(_t('app.admin.coupons_edit.unable_to_update_the_coupon_an_error_occurred'));
return $scope.errors = err.data; $scope.errors = err.data;
}); });
}; };
@ -120,7 +123,7 @@ Application.Controllers.controller('EditCouponController', ['$scope', '$state',
const initialize = function () { const initialize = function () {
// parse the date if any // parse the date if any
if (couponPromise.valid_until) { 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) { $scope.removeElement = function (model, index) {
if ((model === 'category') && (getModel(model)[1].length === 1)) { 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; return false;
} }
if (getModel(model)[1][index].related_to > 0) { 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 false;
} }
return dialogs.confirm({ return dialogs.confirm({
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.events.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_ELEMENT', { ELEMENT: model }, 'messageformat') msg: _t('app.admin.events.do_you_really_want_to_delete_this_ELEMENT', { ELEMENT: model })
}; };
} }
} }
} }
, function () { // delete confirmed , function () { // delete confirmed
getModel(model)[0].delete(getModel(model)[1][index], null, function () { getModel(model)[1].splice(index, 1); } 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 // save the price category to the API
PriceCategory.save(p_cat, function (cat) { PriceCategory.save(p_cat, function (cat) {
$scope.priceCategories.push(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) { , 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); return console.error(err);
}); });
}); });
@ -308,7 +308,7 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
*/ */
$scope.editPriceCategory = function (id, index) { $scope.editPriceCategory = function (id, index) {
if ($scope.priceCategories[index].id !== id) { 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 { } else {
return $uibModal.open({ return $uibModal.open({
templateUrl: '<%= asset_path "admin/events/price_form.html" %>', 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 // update the price category to the API
PriceCategory.update({ id }, { price_category: p_cat }, function (cat) { PriceCategory.update({ id }, { price_category: p_cat }, function (cat) {
$scope.priceCategories[index] = 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) { , 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); return console.error(err);
}); });
}); });
@ -337,17 +337,17 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
*/ */
$scope.removePriceCategory = function (id, index) { $scope.removePriceCategory = function (id, index) {
if ($scope.priceCategories[index].id !== id) { 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) { } 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 { } else {
return dialogs.confirm( return dialogs.confirm(
{ {
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.events.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_price_category') 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( PriceCategory.remove(
{ id }, { id },
function () { // successfully deleted 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); $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; $scope.event = eventPromise;
// list of reservations for the current event // 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 // Possible types of recurrences for an event
$scope.recurrenceTypes = [ $scope.recurrenceTypes = [
{ label: _t('none'), value: 'none' }, { label: _t('app.admin.events_new.none'), value: 'none' },
{ label: _t('every_days'), value: 'day' }, { label: _t('app.admin.events_new.every_days'), value: 'day' },
{ label: _t('every_week'), value: 'week' }, { label: _t('app.admin.events_new.every_week'), value: 'week' },
{ label: _t('every_month'), value: 'month' }, { label: _t('app.admin.events_new.every_month'), value: 'month' },
{ label: _t('every_year'), value: 'year' } { label: _t('app.admin.events_new.every_year'), value: 'year' }
]; ];
// Using the EventsController // Using the EventsController

View File

@ -25,10 +25,10 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro
const CHART_HEIGHT = 500; const CHART_HEIGHT = 500;
// Label of the charts' horizontal axes // 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 // 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 // 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']; 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') { } else if ($scope.display.interval === 'week') {
if ((typeof x === 'number') || d instanceof Date) { 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') { } 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' } else { // typeof d == 'string'
return d; return d;
} }
@ -653,7 +653,7 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro
// common for each charts // common for each charts
chart.margin({ left: 100, right: 100 }); 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); chart.height(CHART_HEIGHT);
// add new chart to the page // add new chart to the page

View File

@ -54,15 +54,15 @@ Application.Controllers.controller('GroupsController', ['$scope', 'groupsPromise
*/ */
$scope.saveGroup = function (data, id) { $scope.saveGroup = function (data, id) {
if (id != null) { if (id != null) {
return Group.update({ id }, { group: data }, response => growl.success(_t('group_form.changes_successfully_saved')) return Group.update({ id }, { group: data }, response => growl.success(_t('app.admin.members.group_form.changes_successfully_saved'))
, error => growl.error(_t('group_form.an_error_occurred_while_saving_changes'))); , error => growl.error(_t('app.admin.members.group_form.an_error_occurred_while_saving_changes')));
} else { } else {
return Group.save({ group: data }, function (resp) { 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; return $scope.groups[$scope.groups.length - 1].id = resp.id;
} }
, function (error) { , 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); return $scope.groups.splice($scope.groups.length - 1, 1);
}); });
} }
@ -74,10 +74,10 @@ Application.Controllers.controller('GroupsController', ['$scope', 'groupsPromise
*/ */
$scope.removeGroup = index => $scope.removeGroup = index =>
Group.delete({ id: $scope.groups[index].id }, function (resp) { 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); 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 * Enable/disable the group at the specified index
@ -86,13 +86,13 @@ Application.Controllers.controller('GroupsController', ['$scope', 'groupsPromise
return $scope.toggleDisableGroup = function (index) { return $scope.toggleDisableGroup = function (index) {
const group = $scope.groups[index]; const group = $scope.groups[index];
if (!group.disabled && (group.users > 0)) { 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 { } else {
return Group.update({ id: group.id }, { group: { disabled: !group.disabled } }, function (response) { return Group.update({ id: group.id }, { group: { disabled: !group.disabled } }, function (response) {
$scope.groups[index] = 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); $scope.invoices.unshift(res.avoir);
return Invoice.get({ id: invoice.id }, function (data) { return Invoice.get({ id: invoice.id }, function (data) {
invoice.has_avoir = data.has_avoir; 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) { modalInstance.result.then(function (model) {
Setting.update({ name: 'invoice_reference' }, { value: model }, function (data) { Setting.update({ name: 'invoice_reference' }, { value: model }, function (data) {
$scope.invoice.reference.model = model; $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) { , 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); 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) { Setting.update({ name: 'invoice_code-value' }, { value: result.model }, function (data) {
$scope.invoice.code.model = result.model; $scope.invoice.code.model = result.model;
if (result.active) { 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) { , 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 console.error(error);
}); });
return Setting.update({ name: 'invoice_code-active' }, { value: result.active ? 'true' : 'false' }, function (data) { return Setting.update({ name: 'invoice_code-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
$scope.invoice.code.active = result.active; $scope.invoice.code.active = result.active;
if (result.active) { if (result.active) {
return growl.success(_t('invoices.code_successfully_activated')); return growl.success(_t('app.admin.invoices.code_successfully_activated'));
} else { } else {
return growl.success(_t('invoices.code_successfully_disabled')); return growl.success(_t('app.admin.invoices.code_successfully_disabled'));
} }
} }
, function (error) { , 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); return console.error(error);
}); });
}); });
@ -373,10 +373,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return modalInstance.result.then(function (model) { return modalInstance.result.then(function (model) {
Setting.update({ name: 'invoice_order-nb' }, { value: model }, function (data) { Setting.update({ name: 'invoice_order-nb' }, { value: model }, function (data) {
$scope.invoice.number.model = model; $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) { , 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); 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) { Setting.update({ name: 'invoice_VAT-rate' }, { value: result.rate + '' }, function (data) {
$scope.invoice.VAT.rate = result.rate; $scope.invoice.VAT.rate = result.rate;
if (result.active) { 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) { , 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 console.error(error);
}); });
return Setting.update({ name: 'invoice_VAT-active' }, { value: result.active ? 'true' : 'false' }, function (data) { return Setting.update({ name: 'invoice_VAT-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
$scope.invoice.VAT.active = result.active; $scope.invoice.VAT.active = result.active;
if (result.active) { if (result.active) {
return growl.success(_t('invoices.VAT_successfully_activated')); return growl.success(_t('app.admin.invoices.VAT_successfully_activated'));
} else { } else {
return growl.success(_t('invoices.VAT_successfully_disabled')); return growl.success(_t('app.admin.invoices.VAT_successfully_disabled'));
} }
} }
, function (error) { , 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); return console.error(error);
}); });
}); });
@ -461,10 +461,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
const parsed = parseHtml($scope.invoice.text.content); const parsed = parseHtml($scope.invoice.text.content);
return Setting.update({ name: 'invoice_text' }, { value: parsed }, function (data) { return Setting.update({ name: 'invoice_text' }, { value: parsed }, function (data) {
$scope.invoice.text.content = parsed; $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) { , 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); return console.error(error);
}); });
}; };
@ -476,10 +476,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
const parsed = parseHtml($scope.invoice.legals.content); const parsed = parseHtml($scope.invoice.legals.content);
return Setting.update({ name: 'invoice_legals' }, { value: parsed }, function (data) { return Setting.update({ name: 'invoice_legals' }, { value: parsed }, function (data) {
$scope.invoice.legals.content = parsed; $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) { , 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); return console.error(error);
}); });
}; };
@ -552,7 +552,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
$scope.save = function() { $scope.save = function() {
Setting.bulkUpdate( Setting.bulkUpdate(
{ settings: Object.values($scope.settings) }, { 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) { function (error) {
growl.error('unexpected_error_occurred'); growl.error('unexpected_error_occurred');
console.error(error); console.error(error);
@ -591,9 +591,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return Setting.update( return Setting.update(
{ name: 'invoice_logo' }, { name: 'invoice_logo' },
{ value: $scope.invoice.logo.base64 }, { 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) { 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); return console.error(error);
} }
); );
@ -696,17 +696,17 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
// Possible refunding methods // Possible refunding methods
$scope.avoirModes = [ $scope.avoirModes = [
{ name: _t('invoices.none'), value: 'none' }, { name: _t('app.admin.invoices.none'), value: 'none' },
{ name: _t('invoices.by_cash'), value: 'cash' }, { name: _t('app.admin.invoices.by_cash'), value: 'cash' },
{ name: _t('invoices.by_cheque'), value: 'cheque' }, { name: _t('app.admin.invoices.by_cheque'), value: 'cheque' },
{ name: _t('invoices.by_transfer'), value: 'transfer' }, { name: _t('app.admin.invoices.by_transfer'), value: 'transfer' },
{ name: _t('invoices.by_wallet'), value: 'wallet' } { name: _t('app.admin.invoices.by_wallet'), value: 'wallet' }
]; ];
// If a subscription was took with the current invoice, should it be canceled or not // If a subscription was took with the current invoice, should it be canceled or not
$scope.subscriptionExpireOptions = {}; $scope.subscriptionExpireOptions = {};
$scope.subscriptionExpireOptions[_t('yes')] = true; $scope.subscriptionExpireOptions[_t('app.shared.buttons.yes')] = true;
$scope.subscriptionExpireOptions[_t('no')] = false; $scope.subscriptionExpireOptions[_t('app.shared.buttons.no')] = false;
// AngularUI-Bootstrap datepicker parameters to define when to refund // AngularUI-Bootstrap datepicker parameters to define when to refund
$scope.datePicker = { $scope.datePicker = {
@ -742,7 +742,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
} }
if ($scope.avoir.invoice_items_ids.length === 0) { 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 { } else {
return Invoice.save( return Invoice.save(
{ avoir: $scope.avoir }, { avoir: $scope.avoir },
@ -750,7 +750,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
$uibModalInstance.close({ avoir, invoice: $scope.invoice }); $uibModalInstance.close({ avoir, invoice: $scope.invoice });
}, },
function (err) { // failed 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) { 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: { resolve: {
object () { object () {
return { return {
title: _t('invoices.confirmation_required'), title: _t('app.admin.invoices.confirmation_required'),
msg: $sce.trustAsHtml( msg: $sce.trustAsHtml(
_t( _t(
'invoices.confirm_close_START_END', 'invoices.confirm_close_START_END',
{ START: moment.utc($scope.period.start_at).format('LL'), END: moment.utc($scope.period.end_at).format('LL') } { START: moment.utc($scope.period.start_at).format('LL'), END: moment.utc($scope.period.end_at).format('LL') }
) )
+ '<br/><br/><strong>' + '<br/><br/><strong>'
+ _t('invoices.period_must_match_fiscal_year') + _t('app.admin.invoices.period_must_match_fiscal_year')
+ '</strong><br/><br/>' + '</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) { function(error) {
$scope.pendingCreation = false; $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; $scope.errors = error.data;
} }
); );
@ -982,7 +982,7 @@ Application.Controllers.controller('AccountingExportModalController', ['$scope',
Export.status(statusQry).then(function (res) { Export.status(statusQry).then(function (res) {
if (!res.data.exists) { if (!res.data.exists) {
growl.success(_t('invoices.export_is_running')); growl.success(_t('app.admin.invoices.export_is_running'));
} }
$uibModalInstance.close(res); $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 * Ask for confirmation then delete the specified administrator
* @param admins {Array} full list of administrators * @param admins {Array} full list of administrators
@ -193,8 +222,8 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.members.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')) 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( Admin.delete(
{ id: admin.id }, { id: admin.id },
function () { function () {
admins.splice(findAdminIdxById(admins, admin.id), 1); admins.splice(findItemIdxById(admins, admin.id), 1);
return growl.success(_t('administrator_successfully_deleted')); 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) { $scope.alertExport = function (type) {
Export.status({ category: 'users', type }).then(function (res) { Export.status({ category: 'users', type }).then(function (res) {
if (!res.data.exists) { 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; var searchTimeout = null;
/** /**
* Iterate through the provided array and return the index of the requested admin * Iterate through the provided array and return the index of the requested item
* @param admins {Array} full list of users with role 'admin' * @param items {Array} full list of users with role 'admin'
* @param id {Number} user id of the admin to retrieve in the list * @param id {Number} id of the item to retrieve in the list
* @returns {Number} index of the requested admin, in the provided array * @returns {Number} index of the requested item, in the provided array
*/ */
var findAdminIdxById = function (admins, id) { var findItemIdxById = function (items, id) {
return (admins.map(function (admin) { return admin.id; })).indexOf(id); return (items.map(function (item) { return item.id; })).indexOf(id);
}; };
/** /**
@ -395,10 +424,10 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
{ id: subscription.id }, { id: subscription.id },
{ subscription: { expired_at: $scope.new_expired_at, free } }, { subscription: { expired_at: $scope.new_expired_at, free } },
function (_subscription) { 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); 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.ok = function () {
$scope.subscription.user_id = user.id; $scope.subscription.user_id = user.id;
return Subscription.save({ }, { subscription: $scope.subscription }, function (_subscription) { 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); $uibModalInstance.close(_subscription);
return $state.reload(); return $state.reload();
} }
, function (error) { , 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); console.error(error);
}); });
}; };
@ -458,7 +487,7 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); }; $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; }); return modalInstance.result.then(function (subscription) { $scope.subscription = subscription; });
}; };
@ -507,11 +536,11 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
avoir_description: $scope.description avoir_description: $scope.description
}, },
function (_wallet) { function (_wallet) {
growl.success(_t('wallet_credit_successfully')); growl.success(_t('app.shared.wallet.wallet_credit_successfully'));
return $uibModalInstance.close(_wallet); return $uibModalInstance.close(_wallet);
}, },
function (error) { 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); console.error(error);
} }
); );
@ -721,11 +750,12 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
{}, {},
{ admin: $scope.admin }, { admin: $scope.admin },
function () { 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'); return $state.go('app.admin.members');
} }
, function (error) { , 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) { if (client.id != null) {
OpenAPIClient.update({ id: client.id }, { open_api_client: client }, function (clientResp) { OpenAPIClient.update({ id: client.id }, { open_api_client: client }, function (clientResp) {
client = clientResp; client = clientResp;
return growl.success(_t('client_successfully_updated')); return growl.success(_t('app.admin.open_api_clients.client_successfully_updated'));
}); });
} else { } else {
OpenAPIClient.save({ open_api_client: client }, function (client) { OpenAPIClient.save({ open_api_client: client }, function (client) {
$scope.clients.push(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: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.open_api_clients.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_open_api_client') 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 () { OpenAPIClient.delete({ id: $scope.clients[index].id }, function () {
$scope.clients.splice(index, 1); $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: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.open_api_clients.confirmation_required'),
msg: _t('do_you_really_want_to_revoke_this_open_api_access') 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) { OpenAPIClient.resetToken({ id: client.id }, {}, function (clientResp) {
client.token = clientResp.token; 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); $uibModalInstance.close($scope.partner);
}, },
function (error) { 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); console.error(error);
} }
); );
@ -143,9 +143,9 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
*/ */
$scope.afterSubmit = function (content) { $scope.afterSubmit = function (content) {
if ((content.id == null) && (content.plan_ids == null)) { 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 { } 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) { if (content.plan_ids != null) {
return $state.go('app.admin.pricing'); return $state.go('app.admin.pricing');
} else { } else {
@ -237,9 +237,9 @@ Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'p
*/ */
$scope.afterSubmit = function (content) { $scope.afterSubmit = function (content) {
if ((content.id == null) && (content.plan_ids == null)) { 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 { } 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'); return $state.go('app.admin.pricing');
} }
}; };

View File

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

View File

@ -166,7 +166,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
Setting.update( Setting.update(
{ name: setting.name }, { name: setting.name },
{ value }, { 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); } function (error) { console.log(error); }
); );
}; };
@ -191,7 +191,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
// reset history // reset history
$scope.privacyDraftsHistory = []; $scope.privacyDraftsHistory = [];
data.setting.history.forEach(function (draft) { 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') { if (type === 'privacy_draft') {
const orderedHistory = $filter('orderBy')(data.setting.history, 'created_at'); 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); }) angular.forEach(v, function(err) { growl.error(err); })
}); });
} else { } else {
growl.success(_t('settings.file_successfully_updated')); growl.success(_t('app.admin.settings.file_successfully_updated'));
if (content.custom_asset.name === 'cgu-file') { if (content.custom_asset.name === 'cgu-file') {
$scope.cguFile = content.custom_asset; $scope.cguFile = content.custom_asset;
$scope.methods.cgu = 'put'; $scope.methods.cgu = 'put';
@ -316,7 +316,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$filter', '
} }
privacyDraftsPromise.setting.history.forEach(function (draft) { 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 () { $scope.publish = function () {
saveCb({ name: 'privacy_body', value: privacyPolicy.bodyTemp }); 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'); $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) * Callback to open the datepicker (interval start)
* @param $event {Object} jQuery event object * @param $event {Object} jQuery event object
@ -215,10 +222,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
*/ */
$scope.formatSex = function (sex) { $scope.formatSex = function (sex) {
if (sex === 'male') { if (sex === 'male') {
return _t('man'); return _t('app.admin.statistics.man');
} }
if (sex === 'female') { 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 index {String} elasticSearch document type (account|event|machine|project|subscription|training)
* @param type {String} statistics type (month|year|booking|hour|user|project) * @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 * @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) * properties and on index-specific properties (additional_fields)
*/ */
var buildCustomFiltersList = function () { var buildCustomFiltersList = function () {
$scope.filters = [ $scope.filters = [
{ key: 'date', label: _t('date'), values: ['input_date'] }, { key: 'date', label: _t('app.admin.statistics.date'), values: ['input_date'] },
{ key: 'userId', label: _t('user_id'), values: ['input_number'] }, { key: 'userId', label: _t('app.admin.statistics.user_id'), values: ['input_number'] },
{ key: 'gender', label: _t('gender'), values: [{ key: 'male', label: _t('man') }, { key: 'female', label: _t('woman') }] }, { 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('age'), values: ['input_number'] }, { key: 'age', label: _t('app.admin.statistics.age'), values: ['input_number'] },
{ key: 'subType', label: _t('type'), values: $scope.type.active.subtypes }, { key: 'subType', label: _t('app.admin.statistics.type'), values: $scope.type.active.subtypes },
{ key: 'ca', label: _t('revenue'), values: ['input_number'] } { key: 'ca', label: _t('app.admin.statistics.revenue'), values: ['input_number'] }
]; ];
if (!$scope.type.active.simple) { if (!$scope.type.active.simple) {
@ -709,7 +716,7 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u
Export.status(statusQry).then(function (res) { Export.status(statusQry).then(function (res) {
if (!res.data.exists) { 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 * DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md * 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 // List of users's tags
$scope.tags = tagsPromise; $scope.tags = tagsPromise;
@ -44,15 +44,15 @@ Application.Controllers.controller('TagsController', ['$scope', 'tagsPromise', '
*/ */
$scope.saveTag = function (data, id) { $scope.saveTag = function (data, id) {
if (id != null) { if (id != null) {
return Tag.update({ id }, { tag: data }, response => growl.success(_t('changes_successfully_saved')) return Tag.update({ id }, { tag: data }, response => growl.success(_t('app.admin.members.tag_form.changes_successfully_saved'))
, error => growl.error(_t('an_error_occurred_while_saving_changes'))); , error => growl.error(_t('app.admin.members.tag_form.an_error_occurred_while_saving_changes')));
} else { } else {
return Tag.save({ tag: data }, function (resp) { 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; return $scope.tags[$scope.tags.length - 1].id = resp.id;
} }
, function (error) { , 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); 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 * Deletes the tag at the specified index
* @param index {number} tag index in the $scope.tags array * @param index {number} tag index in the $scope.tags array
*/ */
return $scope.removeTag = index => $scope.removeTag = index =>
// TODO add confirmation : les utilisateurs seront déasociés 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) { 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); 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); 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: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.admin.trainings.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_training') 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 function () { // deletion confirmed
training.$delete(function () { training.$delete(function () {
$scope.trainings.splice(index, 1); $scope.trainings.splice(index, 1);
growl.info(_t('training_successfully_deleted')); growl.info(_t('app.admin.trainings.training_successfully_deleted'));
}, },
function (error) { 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); console.error(error);
}); });
} }

View File

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

View File

@ -16,8 +16,8 @@
* Controller used in the public calendar global * 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', 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) { function ($scope, $state, $aside, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise, iCalendarPromise) {
/* PRIVATE STATIC CONSTANTS */ /* PRIVATE STATIC CONSTANTS */
let currentMachineEvent = null; let currentMachineEvent = null;
machinesPromise.forEach(m => m.checked = true); machinesPromise.forEach(m => m.checked = true);
@ -38,6 +38,9 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
// List of spaces // List of spaces
$scope.spaces = spacesPromise.filter(t => !t.disabled); $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 // add availabilities source to event sources
$scope.eventSources = []; $scope.eventSources = [];
@ -48,10 +51,41 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
trainings: isSelectAll('trainings', scope), trainings: isSelectAll('trainings', scope),
machines: isSelectAll('machines', scope), machines: isSelectAll('machines', scope),
spaces: isSelectAll('spaces', scope), spaces: isSelectAll('spaces', scope),
externals: isSelectAll('externals', scope),
evt: filter.evt, evt: filter.evt,
dispo: filter.dispo 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 // 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), trainings: isSelectAll('trainings', $scope),
machines: isSelectAll('machines', $scope), machines: isSelectAll('machines', $scope),
spaces: isSelectAll('spaces', $scope), spaces: isSelectAll('spaces', $scope),
externals: isSelectAll('externals', $scope),
evt: true, evt: true,
dispo: true dispo: true
}; };
@ -85,6 +120,9 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
spaces () { spaces () {
return $scope.spaces; return $scope.spaces;
}, },
externals () {
return $scope.externals;
},
filter () { filter () {
return $scope.filter; return $scope.filter;
}, },
@ -95,10 +133,11 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
return $scope.filterAvailabilities; 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.trainings = trainings;
$scope.machines = machines; $scope.machines = machines;
$scope.spaces = spaces; $scope.spaces = spaces;
$scope.externals = externals;
$scope.filter = filter; $scope.filter = filter;
$scope.toggleFilter = (type, filter) => toggleFilter(type, filter); $scope.toggleFilter = (type, filter) => toggleFilter(type, filter);
@ -114,25 +153,66 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
/* PRIVATE SCOPE */ /* 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) { const calendarEventClickCb = function (event, jsEvent, view) {
// current calendar object // current calendar object
const { calendar } = uiCalendarConfig.calendars; const { calendar } = uiCalendarConfig.calendars;
if (event.available_type === 'machines') { if (event.available_type === 'machines') {
currentMachineEvent = event; currentMachineEvent = event;
calendar.fullCalendar('changeView', 'agendaDay'); calendar.fullCalendar('changeView', 'agendaDay');
return calendar.fullCalendar('gotoDate', event.start); calendar.fullCalendar('gotoDate', event.start);
} else if (event.available_type === 'space') { } else if (event.available_type === 'space') {
calendar.fullCalendar('changeView', 'agendaDay'); calendar.fullCalendar('changeView', 'agendaDay');
return calendar.fullCalendar('gotoDate', event.start); calendar.fullCalendar('gotoDate', event.start);
} else if (event.available_type === 'event') { } 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') { } 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 { } else {
if (event.machine_id) { 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) { } 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) { const viewRenderCb = function (view, element) {
toggleSlotEventOverlap(view); toggleSlotEventOverlap(view);
if (view.type === 'agendaDay') { 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) { const eventRenderCb = function (event, element) {
if (event.tags.length > 0) { if (event.tags && event.tags.length > 0) {
let html = ''; let html = '';
for (let tag of Array.from(event.tags)) { for (let tag of Array.from(event.tags)) {
html += `<span class='label label-success text-white'>${tag.name}</span> `; 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())}`; 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 // !!! MUST BE CALLED AT THE END of the controller
return initialize(); 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', 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, 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 */ /* PUBLIC SCOPE */
// reservations for the currently shown event // reservations for the currently shown event
$scope.reservations = []; $scope.reservations = [];
// current date & time
$scope.now = moment();
// user to deal with // user to deal with
$scope.ctrl = $scope.ctrl =
{ member: {} }; { 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 // Global config: delay in hours before a booking while changing the booking slot is forbidden
$scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay); $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 // Message displayed to the end user about rules that applies to events reservations
$scope.eventExplicationsAlert = settingsPromise.event_explications_alert; $scope.eventExplicationsAlert = settingsPromise.event_explications_alert;
@ -178,8 +187,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.public.events_show.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_event') 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 // the admin has confirmed, delete
event.$delete(function () { event.$delete(function () {
$state.go('app.public.events_list'); $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) { }, function (error) {
console.error(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); const amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount);
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) { if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) { if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('online_payment_disabled')); growl.error(_t('app.public.events_show.online_payment_disabled'));
} else { } else {
return payByStripe(reservation); return payByStripe(reservation);
} }
@ -291,7 +300,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}); });
} else { } else {
// otherwise we alert, this error musn't occur when the current user is not admin // 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 * 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) * a new date for his reservation (if any available)
* @param reservation {{id:number, reservable_id:number, nb_reserve_places:number}} * @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) { $scope.modifyReservation = function (reservation) {
e.preventDefault();
e.stopPropagation();
const index = $scope.reservations.indexOf(reservation); const index = $scope.reservations.indexOf(reservation);
return $uibModal.open({ return $uibModal.open({
templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>', templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>',
@ -364,9 +407,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
$scope.reservation = angular.copy(reservation); $scope.reservation = angular.copy(reservation);
// set the reservable_id to the first available event // set the reservable_id to the first available event
for (e of Array.from(event.recurrence_events)) { for (evt of Array.from(event.recurrence_events)) {
if (e.nb_free_places > reservation.total_booked_seats) { if (evt.nb_free_places > reservation.total_booked_seats) {
$scope.reservation.reservable_id = e.id; $scope.reservation.reservable_id = evt.id;
break; break;
} }
} }
@ -419,7 +462,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
/** /**
* Checks if the provided reservation is able to be moved (date change) * 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) { $scope.reservationCanModify = function (reservation) {
const slotStart = moment(reservation.slots[0].start_at); 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)); 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 * Compute the total amount for the current reservation according to the previously set parameters
* and assign the result in $scope.reserve.amountTotal * 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 () { var resetEventReserve = function () {
if ($scope.event) { if ($scope.event) {
@ -696,12 +750,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// Button label // Button label
if ($scope.amount > 0) { 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 { } else {
if ((price.price > 0) && ($scope.walletAmount === 0)) { 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 { } 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 * What to do after the payment was successful
* @param resveration {Object} booked reservation * @param reservation {Object} booked reservation
*/ */
var afterPayment = function (reservation) { var afterPayment = function (reservation) {
$scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats; $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 = ''; let text = '';
angular.forEach($scope.machine.trainings, function (training) { angular.forEach($scope.machine.trainings, function (training) {
if (text.length > 0) { 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); 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) { $scope.delete = function (machine) {
// check the permissions // check the permissions
if ($scope.currentUser.role !== 'admin') { if ($scope.currentUser.role !== 'admin') {
console.error(_t('unauthorized_operation')); console.error(_t('app.public.machines_show.unauthorized_operation'));
} else { } else {
dialogs.confirm({ dialogs.confirm({
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.public.machines_show.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_machine') 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 // delete the machine then redirect to the machines listing
machine.$delete( machine.$delete(
function () { $state.go('app.public.machines_list'); }, 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; $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.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = FREE_SLOT_BORDER_COLOR; $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(); 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) { $scope.markSlotAsRemoved = function (slot) {
slot.backgroundColor = 'white'; slot.backgroundColor = 'white';
@ -431,16 +431,16 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.slotCancelled = function () { $scope.markSlotAsRemoved($scope.selectedEvent); }; $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.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee'; $scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('i_change'); $scope.selectedEvent.title = _t('app.logged.machines_reserve.i_change');
return updateCalendar(); 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 () { $scope.changeModifyMachineSlot = function () {
if ($scope.events.placable) { 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)) { if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb'; $scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('i_shift'); $scope.selectedEvent.title = _t('app.logged.machines_reserve.i_shift');
} }
return updateCalendar(); 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. * When modifying an already booked reservation, callback when the modification was successfully done.
*/ */
$scope.modifyMachineSlot = function () { $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.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor; $scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id; $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 () { $scope.cancelModifyMachineSlot = function () {
if ($scope.events.placable) { if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'; $scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = ''; $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'; $scope.events.modifiable.backgroundColor = 'white';
return updateCalendar(); 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- * @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/ */
$scope.doNotSubscribePlan = function (e) { $scope.doNotSubscribePlan = function (e) {
@ -539,11 +539,11 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
machineSlot.is_reserved = true; machineSlot.is_reserved = true;
machineSlot.can_modify = true; machineSlot.can_modify = true;
if ($scope.currentUser.role !== 'admin') { 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; machineSlot.borderColor = BOOKED_SLOT_BORDER_COLOR;
updateMachineSlot(machineSlot, reservation, $scope.currentUser); updateMachineSlot(machineSlot, reservation, $scope.currentUser);
} else { } else {
machineSlot.title = _t('not_available'); machineSlot.title = _t('app.logged.machines_reserve.not_available');
machineSlot.borderColor = UNAVAILABLE_SLOT_BORDER_COLOR; machineSlot.borderColor = UNAVAILABLE_SLOT_BORDER_COLOR;
updateMachineSlot(machineSlot, reservation, $scope.ctrl.member); 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. * Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/ * @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/ */

View File

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

View File

@ -142,10 +142,10 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco
$rootScope.currentUser = user; $rootScope.currentUser = user;
Auth._currentUser.group_id = user.group_id; Auth._currentUser.group_id = user.group_id;
$scope.group.change = false; $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) { , 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); return console.error(err);
}); });
@ -198,13 +198,13 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.logged.dashboard.settings.confirmation_required'),
msg: $sce.trustAsHtml( msg: $sce.trustAsHtml(
_t('edit_profile.confirm_delete_your_account') + '<br/>' + _t('app.logged.dashboard.settings.confirm_delete_your_account') + '<br/>' +
'<strong>' + _t('edit_profile.all_data_will_be_lost') + '</strong><br/><br/>' + '<strong>' + _t('app.logged.dashboard.settings.all_data_will_be_lost') + '</strong><br/><br/>' +
_t('edit_profile.invoicing_data_kept') + '<br/>' + _t('app.logged.dashboard.settings.invoicing_data_kept') + '<br/>' +
_t('edit_profile.statistic_data_anonymized') + '<br/>' + _t('app.logged.dashboard.settings.statistic_data_anonymized') + '<br/>' +
_t('edit_profile.no_further_access_to_projects') _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 }, () => Member.remove({ id: user.id }, () =>
Auth.logout().then(function () { Auth.logout().then(function () {
$state.go('app.public.home'); $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) { , function (error) {
console.log(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); const amountToPay = helpers.getAmountToPay($scope.cart.total, wallet.amount);
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) { if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) { if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('online_payment_disabled')); growl.error(_t('app.public.plans.online_payment_disabled'));
} else { } else {
return payByStripe(); return payByStripe();
} }
@ -133,16 +133,16 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
if ($scope.currentUser.role !== 'admin') { if ($scope.currentUser.role !== 'admin') {
$rootScope.currentUser = user; $rootScope.currentUser = user;
Auth._currentUser.group_id = user.group_id; 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 { } 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) { , function (err) {
if ($scope.currentUser.role !== 'admin') { 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 { } 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); console.error(err);
}); });
@ -318,12 +318,12 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
// Button label // Button label
if ($scope.amount > 0) { 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 { } else {
if ((price.price > 0) && ($scope.walletAmount === 0)) { 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 { } 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 , function (data, status) { // failed
$scope.alerts = []; $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; $scope.attempting = false;
} }
); );

View File

@ -123,7 +123,7 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
if (err.data.error) { if (err.data.error) {
growl.error(err.data.error); growl.error(err.data.error);
} else { } 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); console.error(err);
} }
}); });
@ -174,7 +174,7 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
function (email) { function (email) {
// Request the server to send an auth-migration email to the current user // Request the server to send an auth-migration email to the current user
AuthProvider.send_code({ email }, 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); } function (err) { growl.error(err.data.error); }
); );
} }

View File

@ -165,8 +165,8 @@ class ProjectsController {
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.shared.project.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_step') msg: _t('app.shared.project.do_you_really_want_to_delete_this_step')
}; };
} }
} }
@ -215,6 +215,10 @@ class ProjectsController {
return false; return false;
}; };
/**
* This function will query the API to autocomplete the typed user's name
* @param nameLookup {string}
*/
$scope.autoCompleteName = function (nameLookup) { $scope.autoCompleteName = function (nameLookup) {
if (!nameLookup) { if (!nameLookup) {
return; return;
@ -246,6 +250,16 @@ class ProjectsController {
return step.project_step_images_attributes.splice(index, 1); 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); $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) { return OpenlabProject.query({ q: $scope.search.q, page: currentPage, per_page: PROJECTS_PER_PAGE }, function (projectsPromise) {
if (projectsPromise.errors != null) { 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; $scope.openlab.searchOverWholeNetwork = false;
return $scope.triggerSearch(); return $scope.triggerSearch();
} else { } else {
@ -533,8 +547,8 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.public.projects_show.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_project') 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 }); }); $scope.project.$delete(function () { $state.go('app.public.projects_list', {}, { reload: true }); });
}); });
} else { } 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 }, { abuse: $scope.signaler },
function (res) { function (res) {
// creation successful // 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); return $uibModalInstance.close(res);
} }
, function (error) { , function (error) {
// creation failed... // 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(); event.preventDefault();
// check the permissions // check the permissions
if ($scope.currentUser.role !== 'admin') { if ($scope.currentUser.role !== 'admin') {
return console.error(_t('space_show.unauthorized_operation')); return console.error(_t('app.public.space_show.unauthorized_operation'));
} else { } else {
return dialogs.confirm({ return dialogs.confirm({
resolve: { resolve: {
object () { object () {
return { return {
title: _t('space_show.confirmation_required'), title: _t('app.public.space_show.confirmation_required'),
msg: _t('space_show.do_you_really_want_to_delete_this_space') 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'); $state.go('app.public.spaces_list');
}, },
function (error) { 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); console.error(error);
} }
); );
@ -333,7 +333,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
*/ */
$scope.markSlotAsModifying = function () { $scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee'; $scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('space_reserve.i_change'); $scope.selectedEvent.title = _t('app.logged.space_reserve.i_change');
return updateCalendar(); return updateCalendar();
}; };
@ -347,7 +347,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
} }
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) { if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb'; $scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('space_reserve.i_shift'); $scope.selectedEvent.title = _t('app.logged.space_reserve.i_shift');
} }
return updateCalendar(); 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. * When modifying an already booked reservation, callback when the modification was successfully done.
*/ */
$scope.modifyTrainingSlot = function () { $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.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor; $scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id; $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.backgroundColor = 'white';
$scope.events.placable.title = ''; $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'; $scope.events.modifiable.backgroundColor = 'white';
return updateCalendar(); return updateCalendar();
@ -451,7 +451,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
angular.forEach($scope.events.paid, function (spaceSlot, key) { angular.forEach($scope.events.paid, function (spaceSlot, key) {
spaceSlot.is_reserved = true; spaceSlot.is_reserved = true;
spaceSlot.can_modify = 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.backgroundColor = 'white';
spaceSlot.borderColor = RESERVED_SLOT_BORDER_COLOR; spaceSlot.borderColor = RESERVED_SLOT_BORDER_COLOR;
return updateSpaceSlotId(spaceSlot, reservation); return updateSpaceSlotId(spaceSlot, reservation);

View File

@ -47,15 +47,15 @@ Application.Controllers.controller('ShowTrainingController', ['$scope', '$state'
$scope.delete = function (training) { $scope.delete = function (training) {
// check the permissions // check the permissions
if ($scope.currentUser.role !== 'admin') { if ($scope.currentUser.role !== 'admin') {
console.error(_t('unauthorized_operation')); growl.error(_t('app.public.training_show.unauthorized_operation'));
} else { } else {
dialogs.confirm( dialogs.confirm(
{ {
resolve: { resolve: {
object () { object () {
return { return {
title: _t('confirmation_required'), title: _t('app.public.training_show.confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_training') 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( training.$delete(
function () { $state.go('app.public.trainings_list'); }, function () { $state.go('app.public.trainings_list'); },
function (error) { 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); console.error(error);
} }
); );
@ -208,7 +208,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
*/ */
$scope.markSlotAsModifying = function () { $scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee'; $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(); return updateCalendar();
}; };
@ -222,7 +222,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
} }
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) { if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb'; $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(); 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. * When modifying an already booked reservation, callback when the modification was successfully done.
*/ */
$scope.modifyTrainingSlot = function () { $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.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor; $scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id; $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.backgroundColor = 'white';
$scope.events.placable.title = $scope.events.placable.training.name; $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'; $scope.events.modifiable.backgroundColor = 'white';
return updateCalendar(); return updateCalendar();
@ -329,7 +329,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$scope.events.paid[0].can_modify = true; $scope.events.paid[0].can_modify = true;
updateTrainingSlotId($scope.events.paid[0], reservation); updateTrainingSlotId($scope.events.paid[0], reservation);
$scope.events.paid[0].borderColor = '#b2e774'; $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) { if ($scope.selectedPlan) {
$scope.ctrl.member.subscribed_plan = angular.copy($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; return $scope.modePlans = true;
} else { } else {
// otherwise we alert, this error musn't occur when the current user hasn't the admin role // 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); const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
if (!$scope.isAdmin() && (amountToPay > 0)) { if (!$scope.isAdmin() && (amountToPay > 0)) {
if ($rootScope.fablabWithoutOnlinePayment) { if ($rootScope.fablabWithoutOnlinePayment) {
growl.error(_t('cart.online_payment_disabled')); growl.error(_t('app.shared.cart.online_payment_disabled'));
} else { } else {
return payByStripe(reservation); return payByStripe(reservation);
} }
@ -145,7 +145,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}); });
} else { } else {
// otherwise we alert, this error musn't occur when the current user is not admin // 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; return $scope.events.modifiable = null;
} }
, function (err) { // failure , 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); return console.error(err);
}); });
}; };
@ -315,19 +315,19 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
resolve: { resolve: {
object () { object () {
return { return {
title: _t('cart.confirmation_required'), title: _t('app.shared.cart.confirmation_required'),
msg: _t('cart.do_you_really_want_to_cancel_this_reservation') msg: _t('app.shared.cart.do_you_really_want_to_cancel_this_reservation')
}; };
} }
} }
}, },
function () { // cancel confirmed function () { // cancel confirmed
Slot.cancel({ id: $scope.slot.id }, function () { // successfully canceled 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(); } if (typeof $scope.onSlotCancelSuccess === 'function') { return $scope.onSlotCancelSuccess(); }
} }
, function () { // error while canceling , 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 { } else {
// otherwise we alert, this error musn't occur when the current user is not admin // 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; return $scope.amountTotal = null;
} }
}; };
@ -556,12 +556,12 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// Button label // Button label
if ($scope.amount > 0) { 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 { } else {
if ((price.price > 0) && ($scope.walletAmount === 0)) { 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 { } 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) { , function (response) {
$scope.alerts = []; $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; return $scope.attempting = false;
}); });
}; };

View File

@ -53,15 +53,15 @@ Application.Directives.directive('coupon', [ '$rootScope', 'Coupon', '_t', funct
$scope.status = 'valid'; $scope.status = 'valid';
$scope.coupon = res; $scope.coupon = res;
if (res.type === 'percent_off') { 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 { } 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) { , function (err) {
$scope.status = 'invalid'; $scope.status = 'invalid';
$scope.coupon = null; $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'; 'use strict';
/** /**
@ -36,9 +25,9 @@ Application.Directives.directive('selectMember', [ 'Diacritics', 'Member', funct
q['subscription'] = attributes.subscription; q['subscription'] = attributes.subscription;
} }
return Member.search(q, function (users) { Member.search(q, function (users) {
scope.matchingMembers = users; scope.matchingMembers = users;
return scope.isLoadingMembers = false; scope.isLoadingMembers = false;
} }
, function (error) { console.error(error); }); , function (error) { console.error(error); });
}; };

View File

@ -73,7 +73,7 @@ Application.Directives.directive('stripeForm', ['Payment', 'growl', '_t',
if (response.error.statusText) { if (response.error.statusText) {
growl.error(response.error.statusText); growl.error(response.error.statusText);
} else { } else {
growl.error(`${_t('payment_card_error')} ${response.error}`); growl.error(`${_t('app.shared.messages.payment_card_error')} ${response.error}`);
} }
confirmButton.prop('disabled', false); confirmButton.prop('disabled', false);
} else if (response.requires_action) { } 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) { Application.Filters.filter('booleanFormat', [ '_t', function (_t) {
return function (boolean) { return function (boolean) {
if (((typeof boolean === 'boolean') && boolean) || ((typeof boolean === 'string') && (boolean === 'true'))) { if (((typeof boolean === 'boolean') && boolean) || ((typeof boolean === 'string') && (boolean === 'true'))) {
return _t('yes'); return _t('app.shared.buttons.yes');
} else { } 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) { Application.Filters.filter('maxCount', [ '_t', function (_t) {
return function (max) { return function (max) {
if ((typeof max === 'undefined') || (max === null) || ((typeof max === 'number') && (max === 0))) { if ((typeof max === 'undefined') || (max === null) || ((typeof max === 'number') && (max === 0))) {
return _t('unlimited'); return _t('app.admin.pricing.unlimited');
} else { } else {
return max; return max;
} }

View File

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

View File

@ -290,6 +290,11 @@
@include border-radius(3px); @include border-radius(3px);
padding: 5px 10px; padding: 5px 10px;
} }
&.well-disabled {
border-color: $gray-lighter;
background-color: $gray-lighter;
color: $gray-light;
}
} }
.read { .read {
@ -629,3 +634,42 @@ padding: 10px;
.help-block.error { .help-block.error {
color: #ff565d; 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{ body.container{
padding: 0; 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-italic { font-style: italic; }
.text-left { text-align: left !important; }
.text-center { text-align: center; } .text-center { text-align: center; }
.text-right { text-align: right; } .text-right { text-align: right; }
@ -379,6 +380,10 @@ p, .widget p {
justify-content: center; justify-content: center;
} }
.pointer {
cursor: pointer;
}
@media screen and (min-width: $screen-lg-min) { @media screen and (min-width: $screen-lg-min) {
.b-r-lg {border-right: 1px solid $border-color; } .b-r-lg {border-right: 1px solid $border-color; }
.hide-b-r-lg { border: none !important; } .hide-b-r-lg { border: none !important; }

View File

@ -32,11 +32,7 @@
@import "app.buttons"; @import "app.buttons";
@import "app.components"; @import "app.components";
@import "app.plugins"; @import "app.plugins";
@import "modules/invoice"; @import "modules/*";
@import "modules/signup";
@import "modules/abuses";
@import "modules/cookies";
@import "modules/stripe";
@import "app.responsive"; @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>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'manage_abuses.abuses_list' }}</h1> <h1 translate>{{ 'app.admin.manage_abuses.abuses_list' }}</h1>
</section> </section>
</div> </div>
</div> </div>
@ -15,24 +15,24 @@
<section class="m-lg"> <section class="m-lg">
<div class="row m-b-md"> <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"> <ul ng-show="abuses.length > 0">
<li class="abuse" ng-repeat="abuse in abuses"> <li class="abuse" ng-repeat="abuse in abuses">
<div class="signaled"> <div class="signaled">
<a ui-sref="app.public.projects_show({id:abuse.signaled.slug})">{{abuse.signaled.name}}</a>, <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>, <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> <span>{{abuse.signaled.published_at | amDateFormat:'L' }}</span>
<button class="btn btn-success" ng-click="confirmProcess(abuse.id)"> <button class="btn btn-success" ng-click="confirmProcess(abuse.id)">
<i class="fa fa-check"></i> <i class="fa fa-check"></i>
</button> </button>
</div> </div>
<div class="report"> <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>, <span>{{abuse.created_at | amDateFormat:'L' }}</span>,
<a href="mailto:{{abuse.email}}">{{abuse.first_name}} {{abuse.last_name}}</a> <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> <cite>{{ abuse.message }}</cite>
</div> </div>
</li> </li>

View File

@ -7,7 +7,7 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'add_an_administrator' }}</h1> <h1 translate>{{ 'app.admin.admins_new.add_an_administrator' }}</h1>
</section> </section>
</div> </div>
@ -20,12 +20,12 @@
<form role="form" name="adminForm" class="form-horizontal" novalidate> <form role="form" name="adminForm" class="form-horizontal" novalidate>
<section class="panel panel-default bg-light m-lg"> <section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r"> <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>
<div class="panel-footer no-padder"> <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> </div>
</section> </section>
</form> </form>

View File

@ -1,16 +1,16 @@
<div class="modal-header"> <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>
<div class="modal-body m-lg"> <div class="modal-body m-lg">
<div> <div>
<span translate>{{ 'expected_data_type' }}</span> : {{datatype}} <span translate>{{ 'app.shared.authentication.expected_data_type' }}</span> : {{datatype}}
</div> </div>
<form name="mappingForm" class="m-t-md"> <form name="mappingForm" class="m-t-md">
<ng-switch on="datatype"> <ng-switch on="datatype">
<!-- BOOLEAN --> <!-- BOOLEAN -->
<div ng-switch-when="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"> <ul class="list-unstyled">
<li class="m-t-sm m-l"> <li class="m-t-sm m-l">
<input type="text" <input type="text"
@ -35,7 +35,7 @@
<!-- DATE --> <!-- DATE -->
<div ng-switch-when="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" <select name="date_format"
id="date_format" id="date_format"
class="form-control" class="form-control"
@ -46,7 +46,7 @@
<!-- INTEGER --> <!-- INTEGER -->
<div ng-switch-when="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> <button class="btn btn-default pull-right" ng-click="addIntegerMapping()"><i class="fa fa-plus"></i></button>
<ul class="list-unstyled"> <ul class="list-unstyled">
<li ng-repeat="map in transformation.rules.mapping" class="m-t-sm m-l"> <li ng-repeat="map in transformation.rules.mapping" class="m-t-sm m-l">
@ -60,6 +60,6 @@
</form> </form>
</div> </div>
<div class="modal-footer"> <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-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>{{ 'cancel' }}</button> <button class="btn btn-warning" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div> </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}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.name" ng-model="provider.name"
@ -8,23 +8,23 @@
id="provider_name" id="provider_name"
ng-disabled="mode == 'edition'" ng-disabled="mode == 'edition'"
required /> 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> </div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[providable_type]'].$dirty && providerForm['auth_provider[providable_type]'].$invalid}"> <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"> <div class="col-sm-9">
<select ng-model="provider.providable_type" <select ng-model="provider.providable_type"
ng-change="updateProvidable()" ng-change="updateProvidable()"
class="form-control" class="form-control"
name="auth_provider[providable_type]" name="auth_provider[providable_type]"
id="provider_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'" ng-disabled="mode == 'edition'"
required> required>
</select> </select>
<input type="hidden" name="auth_provider[type]" ng-value="provider.type" /> <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>
</div> </div>

View File

@ -1,7 +1,7 @@
<hr/> <hr/>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[base_url]'].$dirty && providerForm['auth_provider[base_url]'].$invalid}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.providable_attributes.base_url" ng-model="provider.providable_attributes.base_url"
@ -11,13 +11,13 @@
placeholder="https://sso.example.net..." placeholder="https://sso.example.net..."
required required
url> 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]'].$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>{{ 'provided_url_is_not_a_valid_url' }}</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> </div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[authorization_endpoint]'].$dirty && providerForm['auth_provider[authorization_endpoint]'].$invalid}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.providable_attributes.authorization_endpoint" ng-model="provider.providable_attributes.authorization_endpoint"
@ -27,13 +27,13 @@
placeholder="/oauth2/auth..." placeholder="/oauth2/auth..."
required required
endpoint> 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]'].$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>{{ 'provided_endpoint_is_not_valid' }}</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> </div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[token_endpoint]'].$dirty && providerForm['auth_provider[token_endpoint]'].$invalid}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.providable_attributes.token_endpoint" ng-model="provider.providable_attributes.token_endpoint"
@ -43,13 +43,13 @@
placeholder="/oauth2/token..." placeholder="/oauth2/token..."
required required
endpoint> 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]'].$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>{{ 'provided_endpoint_is_not_valid' }}</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> </div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[profile_url]'].$dirty && providerForm['auth_provider[profile_url]'].$invalid}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.providable_attributes.profile_url" ng-model="provider.providable_attributes.profile_url"
@ -59,13 +59,13 @@
placeholder="https://exemple.net/user..." placeholder="https://exemple.net/user..."
required required
url> 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]'].$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>{{ 'provided_url_is_not_a_valid_url' }}</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> </div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[client_id]'].$dirty && providerForm['auth_provider[client_id]'].$invalid}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.providable_attributes.client_id" ng-model="provider.providable_attributes.client_id"
@ -73,12 +73,12 @@
name="auth_provider[client_id]" name="auth_provider[client_id]"
id="provider_client_id" id="provider_client_id"
required> 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> </div>
<div class="form-group" ng-class="{'has-error': providerForm['auth_provider[client_secret]'].$dirty && providerForm['auth_provider[client_secret]'].$invalid}"> <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"> <div class="col-sm-9">
<input type="text" <input type="text"
ng-model="provider.providable_attributes.client_secret" ng-model="provider.providable_attributes.client_secret"
@ -86,8 +86,8 @@
name="auth_provider[client_secret]" name="auth_provider[client_secret]"
id="provider_client_secret" id="provider_client_secret"
required> 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>
</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"> <table class="table">
<thead> <thead>
<tr> <tr>
<th translate>{{ 'model' }}</th> <th translate>{{ 'app.shared.oauth2.model' }}</th>
<th translate>{{ 'field' }}</th> <th translate>{{ 'app.shared.oauth2.field' }}</th>
<th translate>{{ 'api_endpoint_url' }}</th> <th translate>{{ 'app.shared.oauth2.api_endpoint_url' }}</th>
<th translate>{{ 'api_type' }}</th> <th translate>{{ 'app.shared.oauth2.api_type' }}</th>
<th translate>{{ 'api_fields' }}</th> <th translate>{{ 'app.shared.oauth2.api_fields' }}</th>
<th style="width: 6.4em;"></th> <th style="width: 6.4em;"></th>
</tr> </tr>
</thead> </thead>

View File

@ -9,7 +9,7 @@
</div> </div>
<div class="col-md-8 b-l b-r"> <div class="col-md-8 b-l b-r">
<section class="heading-title"> <section class="heading-title">
<h1>{{ 'provider' | translate }} {{provider.name}}</h1> <h1>{{ 'app.admin.authentication_edit.provider' | translate }} {{provider.name}}</h1>
</section> </section>
</div> </div>
@ -17,7 +17,7 @@
<div class="col-md-3"> <div class="col-md-3">
<section class="heading-actions wrapper"> <section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate> <div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }} {{ 'app.shared.buttons.cancel' }}
</div> </div>
</section> </section>
@ -35,13 +35,13 @@
<section class="panel panel-default bg-light m-lg"> <section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r"> <div class="panel-body m-r">
<ng-include src="'<%= asset_path 'admin/authentications/_form.html' %>'"></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> <ng-include src="'<%= asset_path "admin/authentications/_oauth2.html"%>'" ng-if="provider.providable_type == 'OAuth2Provider'"></ng-include>
</div> <!-- ./panel-body --> </div> <!-- ./panel-body -->
<div class="panel-footer no-padder"> <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> </div>
</section> </section>
</form> </form>

View File

@ -2,23 +2,23 @@
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span> <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>
</div> </div>
<div class="col-md-12"> <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"> <table class="table">
<thead> <thead>
<tr> <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> <th style="width:10%"></th>
</tr> </tr>
@ -31,7 +31,7 @@
<td>{{ getState(provider.status) }}</td> <td>{{ getState(provider.status) }}</td>
<td> <td>
<button class="btn btn-default" ui-sref="app.admin.authentication_edit({id:provider.id})"> <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>
<button class="btn btn-danger" ng-if="provider.status != 'active'" ng-click="destroyProvider(providers, provider)"> <button class="btn btn-danger" ng-if="provider.status != 'active'" ng-click="destroyProvider(providers, provider)">
<i class="fa fa-trash-o"></i> <i class="fa fa-trash-o"></i>

View File

@ -9,7 +9,7 @@
</div> </div>
<div class="col-md-8 b-l b-r"> <div class="col-md-8 b-l b-r">
<section class="heading-title"> <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> </section>
</div> </div>
@ -17,7 +17,7 @@
<div class="col-md-3"> <div class="col-md-3">
<section class="heading-actions wrapper"> <section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate> <div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }} {{ 'app.shared.buttons.cancel' }}
</div> </div>
</section> </section>
@ -36,13 +36,13 @@
<section class="panel panel-default bg-light m-lg"> <section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r"> <div class="panel-body m-r">
<ng-include src="'<%= asset_path 'admin/authentications/_form.html' %>'"></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> <ng-include src="'<%= asset_path "admin/authentications/_oauth2.html" %>'" ng-if="provider.providable_type == 'OAuth2Provider'"></ng-include>
</div> <!-- ./panel-body --> </div> <!-- ./panel-body -->
<div class="panel-footer no-padder"> <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> </div>
</section> </section>
</form> </form>

View File

@ -5,17 +5,17 @@
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a> <a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
</section> </section>
</div> </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"> <section class="heading-title">
<h1 translate>{{ 'admin_calendar.calendar_management' }}</h1> <h1 translate>{{ 'app.admin.calendar.calendar_management' }}</h1>
</section> </section>
</div> </div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md"> <div class="col-xs-1">
<section class="heading-actions wrapper" ng-class="{'p-s': !fablabWithoutSpaces}"> <section class="heading-actions wrapper">
<span class="badge text-sm bg-formation" ng-class="{'m-t-sm': fablabWithoutSpaces}" translate>{{ 'admin_calendar.trainings' }}</span><br> <a role="button" ui-sref="app.admin.calendar.icalendar" class="btn btn-default b-2x rounded pointer m-t-sm">
<span class="badge text-sm bg-machine" translate>{{ 'admin_calendar.machines' }}</span><br> <i class="fa fa-exchange" aria-hidden="true"></i>
<span class="badge text-sm bg-space" ng-hide="fablabWithoutSpaces" translate>{{ 'admin_calendar.spaces' }}</span> </a>
</section> </section>
</div> </div>
@ -27,6 +27,15 @@
<div class="col-sm-12 col-md-12 col-lg-9"> <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 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>
<div class="col-sm-12 col-md-12 col-lg-3"> <div class="col-sm-12 col-md-12 col-lg-3">
@ -35,17 +44,17 @@
ng-href="api/availabilities/export_index.xlsx" ng-href="api/availabilities/export_index.xlsx"
target="export-frame" target="export-frame"
ng-click="alertExport('index')" ng-click="alertExport('index')"
uib-popover="{{ 'admin_calendar.availabilities_notice' | translate}}" uib-popover="{{ 'app.admin.calendar.availabilities_notice' | translate}}"
popover-trigger="mouseenter" popover-trigger="mouseenter"
popover-placement="bottom"> 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> </a>
<iframe name="export-frame" height="0" width="0" class="none"></iframe> <iframe name="export-frame" height="0" width="0" class="none"></iframe>
</div> </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"> <div class="panel-heading b-b small">
<h3 translate>{{ 'admin_calendar.ongoing_reservations' }}</h3> <h3 translate>{{ 'app.admin.calendar.ongoing_reservations' }}</h3>
</div> </div>
<div class="widget-content no-bg auto wrapper" ng-class="{'reservations-locked': availability.lock}"> <div class="widget-content no-bg auto wrapper" ng-class="{'reservations-locked': availability.lock}">
<ul class="list-unstyled" ng-if="reservations.length > 0"> <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> <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> </li>
</ul> </ul>
<div ng-show="reservations.length == 0" translate>{{ 'admin_calendar.no_reservations' }}</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"/> <span class="m-l-xs" translate>{{ 'admin_calendar.reservations_locked' }}</span></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> </div>
<div class="widget panel b-a m m-t-lg" ng-if="availability.machine_ids.length > 0"> <div class="widget panel b-a m m-t-lg" ng-if="availability.machine_ids.length > 0">
<div class="panel-heading b-b small"> <div class="panel-heading b-b small">
<h3 translate>{{ 'admin_calendar.machines' }}</h3> <h3 translate>{{ 'app.admin.calendar.machines' }}</h3>
</div> </div>
<div class="widget-content no-bg auto wrapper"> <div class="widget-content no-bg auto wrapper">
<ul class="list-unstyled"> <ul class="list-unstyled">
@ -77,26 +86,40 @@
<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" >
<div class="panel-heading b-b small"> <div class="panel-heading b-b small">
<h3 translate>{{ 'admin_calendar.actions' }}</h3> <h3 translate>{{ 'app.admin.calendar.actions' }}</h3>
</div> </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()"> <button class="btn btn-default" ng-click="toggleLockReservations()">
<span ng-hide="availability.lock"> <span ng-hide="availability.lock">
<i class="fa fa-stop" /> <i class="fa fa-stop"></i>
<span class="m-l-xs" translate>{{ 'admin_calendar.block_reservations' }}</span> <span class="m-l-xs" translate>{{ 'app.admin.calendar.block_reservations' }}</span>
</span> </span>
<span ng-show="availability.lock"> <span ng-show="availability.lock">
<i class="fa fa-play" /> <i class="fa fa-play"></i>
<span class="m-l-xs" translate>{{ 'admin_calendar.allow_reservations' }}</span> <span class="m-l-xs" translate>{{ 'app.admin.calendar.allow_reservations' }}</span>
</span> </span>
</button> </button>
<button class="btn btn-default m-t" ng-click="removeSlot()"> <button class="btn btn-default m-t" ng-click="removeSlot()">
<span> <span>
<i class="fa fa-trash" /> <i class="fa fa-trash"></i>
<span class="m-l-xs" translate>{{ 'admin_calendar.delete_slot' }}</span> <span class="m-l-xs" translate>{{ 'app.admin.calendar.delete_slot' }}</span>
</span> </span>
</button> </button>
</div> </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>
</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"> <div class="modal-header">
<h3 class="text-center red"> <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> </h3>
</div> </div>
<div class="modal-body" ng-show="step === 1"> <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="form-group">
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" id="training" name="available_type" value="training" ng-model="availability.available_type"> <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> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" id="machine" name="available_type" value="machines" ng-model="availability.available_type"> <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> </label>
</div> </div>
<div class="radio" ng-hide="fablabWithoutSpaces"> <div class="radio" ng-hide="fablabWithoutSpaces">
<label> <label>
<input type="radio" id="space" name="available_type" value="space" ng-model="availability.available_type" ng-disabled="spaces.length === 0"> <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> </label>
</div> </div>
</div> </div>
@ -29,7 +29,7 @@
<div class="modal-body" ng-show="step === 2"> <div class="modal-body" ng-show="step === 2">
<div ng-show="availability.available_type == 'machines'"> <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"> <div class="form-group m-l-xl">
<label class="checkbox" ng-repeat="machine in machines"> <label class="checkbox" ng-repeat="machine in machines">
@ -43,7 +43,7 @@
</select> </select>
<div class="row m-t"> <div class="row m-t">
<div class="form-group"> <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"> <div class="col-sm-6">
<input type="number" id="nb_places_training" class="form-control" ng-model="availability.nb_total_places"> <input type="number" id="nb_places_training" class="form-control" ng-model="availability.nb_total_places">
</div> </div>
@ -56,7 +56,7 @@
</select> </select>
<div class="row m-t"> <div class="row m-t">
<div class="form-group"> <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"> <div class="col-sm-6">
<input type="number" id="nb_places_space" class="form-control" ng-model="availability.nb_total_places"> <input type="number" id="nb_places_space" class="form-control" ng-model="availability.nb_total_places">
</div> </div>
@ -66,19 +66,19 @@
</div> </div>
<div class="modal-body" ng-show="step === 3"> <div class="modal-body" ng-show="step === 3">
<div id="timeAdjust" class="m-t-sm"> <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="row">
<div class="col-md-3 col-md-offset-2"> <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> <uib-timepicker ng-model="start" hour-step="timepickers.start.hstep" readonly-input="true" minute-step="timepickers.start.mstep" show-meridian="false"></uib-timepicker>
</div> </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"> <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> <uib-timepicker ng-model="end" hour-step="timepickers.end.hstep" readonly-input="true" minute-step="timepickers.end.mstep" show-meridian="false"></uib-timepicker>
</fieldset> </fieldset>
</div> </div>
</div> </div>
<div id="tagAssociate" class="m-t-lg"> <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="row">
<div class="col-sm-12"> <div class="col-sm-12">
<ui-select multiple ng-model="availability.tag_ids" class="form-control"> <ui-select multiple ng-model="availability.tag_ids" class="form-control">
@ -93,13 +93,89 @@
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer" ng-show="step < 3"> <div class="modal-body m-h" ng-show="step === 4">
<button class="btn btn-info" ng-click="previous()" ng-disabled="step === 1" translate>{{ 'admin_calendar.previous' }}</button> <div class="m-t-sm">
<button class="btn btn-info" ng-click="next()" translate>{{ 'admin_calendar.next' }}</button> <p class="text-center font-sbold" translate>{{ 'app.admin.calendar.recurrence' }}</p>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button> <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>
<div class="modal-footer" ng-show="step === 3"> </div>
<button class="btn btn-info" ng-click="previous()" translate>{{ 'admin_calendar.previous' }}</button> </div>
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'confirm' }}</button> <div class="row">
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'cancel' }}</button> <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> </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}"> <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]" <input type="text" id="coupon[name]"
name="coupon[name]" name="coupon[name]"
class="form-control" class="form-control"
ng-model="coupon.name" ng-model="coupon.name"
required="required"/> 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>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[code]'].$dirty && couponForm['coupon[code]'].$invalid}"> <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]" <input type="text" id="coupon[code]"
name="coupon[code]" name="coupon[code]"
class="form-control" class="form-control"
@ -17,25 +17,25 @@
ng-pattern="/^[A-Z0-9\-]+$/" ng-pattern="/^[A-Z0-9\-]+$/"
ng-disabled="mode == 'EDIT'" ng-disabled="mode == 'EDIT'"
required="required"/> 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.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>{{ '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.pattern" translate>{{ 'app.shared.coupon.code_must_be_composed_of_capital_letters_digits_and_or_dashes' }}</span>
</div> </div>
<div class="form-group"> <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]" <select id="coupon[type]"
name="coupon[type]" name="coupon[type]"
class="form-control" class="form-control"
ng-model="coupon.type" ng-model="coupon.type"
ng-disabled="mode == 'EDIT'" ng-disabled="mode == 'EDIT'"
required="required"> required="required">
<option value="percent_off" translate>{{ 'percentage' }}</option> <option value="percent_off" translate>{{ 'app.shared.coupon.percentage' }}</option>
<option value="amount_off" translate>{{ 'amount' }}</option> <option value="amount_off" translate>{{ 'app.shared.coupon.amount' }}</option>
</select> </select>
</div> </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'"> <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"> <div class="input-group">
<input type="number" id="coupon[percent_off]" <input type="number" id="coupon[percent_off]"
name="coupon[percent_off]" name="coupon[percent_off]"
@ -47,13 +47,13 @@
ng-required="coupon.type == 'percent_off'"/> ng-required="coupon.type == 'percent_off'"/>
<span class="input-group-addon"><i class="fa fa-percent"></i></span> <span class="input-group-addon"><i class="fa fa-percent"></i></span>
</div> </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.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>{{ '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.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'app.shared.coupon.percentage_must_be_between_0_and_100' }}</span>
</div> </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'"> <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"> <div class="input-group">
<span class="input-group-addon">{{currencySymbol}}</span> <span class="input-group-addon">{{currencySymbol}}</span>
<input type="number" id="coupon[amount_off]" <input type="number" id="coupon[amount_off]"
@ -64,25 +64,25 @@
ng-disabled="mode == 'EDIT'" ng-disabled="mode == 'EDIT'"
ng-required="coupon.type == 'amount_off'"/> ng-required="coupon.type == 'amount_off'"/>
</div> </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.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>{{ '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.min || couponForm['coupon[percent_off]'].$error.max)" translate>{{ 'app.shared.coupon.percentage_must_be_between_0_and_100' }}</span>
</div> </div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[validity_per_user]'].$dirty && couponForm['coupon[validity_per_user]'].$invalid}"> <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]" <select id="coupon[validity_per_user]"
name="coupon[validity_per_user]" name="coupon[validity_per_user]"
class="form-control" class="form-control"
ng-model="coupon.validity_per_user" ng-model="coupon.validity_per_user"
required="required" required="required"
ng-disabled="mode == 'EDIT'" ng-disabled="mode == 'EDIT'"
ng-options="( validity | translate ) for validity in validities"> ng-options="validityName(validity) for validity in validities">
</select> </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>
<div class="form-group" ng-class="{'has-error': errors['valid_until']}"> <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"> <div class="input-group">
<input type="text" id="coupon[valid_until]" <input type="text" id="coupon[valid_until]"
name="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="help-block error" ng-show="errors['valid_until']">{{ errors['valid_until'].join(' ; ') }}</span>
<span class="text-info text-xs"> <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> </span>
</div> </div>
<div class="form-group" ng-class="{'has-error': couponForm['coupon[max_usages]'].$dirty && couponForm['coupon[max_usages]'].$invalid}"> <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]" <input type="number" id="coupon[max_usages]"
name="coupon[max_usages]" name="coupon[max_usages]"
class="form-control" class="form-control"
ng-model="coupon.max_usages" ng-model="coupon.max_usages"
ng-disabled="mode == 'EDIT'" ng-disabled="mode == 'EDIT'"
min="0"/> 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"> <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> </span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="coupon[active]" translate>{{ 'enabled' }}</label> <label for="coupon[active]" translate>{{ 'app.shared.coupon.enabled' }}</label>
<input bs-switch <input bs-switch
ng-model="coupon.active" ng-model="coupon.active"
id="coupon[active]" id="coupon[active]"
type="checkbox" type="checkbox"
class="form-control" class="form-control"
switch-on-text="{{ 'yes' | translate }}" switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'no' | translate }}" switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true" /> switch-animate="true" />
<input type="hidden" name="coupon[active]" value="{{coupon.active}}"/> <input type="hidden" name="coupon[active]" value="{{coupon.active}}"/>
</div> </div>

View File

@ -2,18 +2,18 @@
<div class="row no-gutter"> <div class="row no-gutter">
<div class="col-xs-2 col-sm-2 col-md-1"> <div class="col-xs-2 col-sm-2 col-md-1">
<section class="heading-btn"> <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> </section>
</div> </div>
<div class="col-xs-7 col-sm-7 col-md-8 b-l"> <div class="col-xs-7 col-sm-7 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1>{{ 'coupon' | translate }} : {{ coupon.name }}</h1> <h1>{{ 'app.admin.coupons_edit.coupon' | translate }} {{ coupon.name }}</h1>
</section> </section>
</div> </div>
<div class="col-xs-3 col-md-3"> <div class="col-xs-3 col-md-3">
<section class="heading-actions wrapper"> <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> </section>
</div> </div>
@ -28,10 +28,10 @@
<div id="couponForm"> <div id="couponForm">
<form name="couponForm" novalidate="novalidate" class="col-lg-7 col-lg-offset-2 m-t-lg form-group"> <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"> <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> </div>
</form> </form>
</div> </div>

View File

@ -2,12 +2,12 @@
<div class="row no-gutter"> <div class="row no-gutter">
<div class="col-xs-2 col-sm-2 col-md-1"> <div class="col-xs-2 col-sm-2 col-md-1">
<section class="heading-btn"> <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> </section>
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'add_a_coupon' }}</h1> <h1 translate>{{ 'app.admin.coupons_new.add_a_coupon' }}</h1>
</section> </section>
</div> </div>
@ -20,10 +20,10 @@
<div id="couponForm"> <div id="couponForm">
<form name="couponForm" novalidate="novalidate" class="col-lg-10 col-lg-offset-2 m-t-lg form-group"> <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"> <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> </div>
</form> </form>

View File

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

View File

@ -7,13 +7,13 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md"> <div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'fablab_events' }}</h1> <h1 translate>{{ 'app.admin.events.fablab_events' }}</h1>
</section> </section>
</div> </div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin'])"> <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"> <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> </section>
</div> </div>
</div> </div>
@ -22,16 +22,16 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<uib-tabset justified="true"> <uib-tabset justified="true">
<uib-tab heading="{{ 'events_monitoring' | translate }}"> <uib-tab heading="{{ 'app.admin.events.events_monitoring' | translate }}">
<ng-include src="'<%= asset_path 'admin/events/monitoring.html' %>'"></ng-include> <ng-include src="'<%= asset_path "admin/events/monitoring.html" %>'"></ng-include>
</uib-tab> </uib-tab>
<uib-tab heading="{{ 'manage_filters' | translate }}"> <uib-tab heading="{{ 'app.admin.events.manage_filters' | translate }}">
<ng-include src="'<%= asset_path 'admin/events/filters.html' %>'"></ng-include> <ng-include src="'<%= asset_path "admin/events/filters.html" %>'"></ng-include>
</uib-tab> </uib-tab>
<uib-tab heading="{{ 'manage_prices_categories' | translate }}"> <uib-tab heading="{{ 'app.admin.events.manage_prices_categories' | translate }}">
<ng-include src="'<%= asset_path 'admin/events/prices.html' %>'"></ng-include> <ng-include src="'<%= asset_path "admin/events/prices.html" %>'"></ng-include>
</uib-tab> </uib-tab>
</uib-tabset> </uib-tabset>
</div> </div>

View File

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

View File

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

View File

@ -1,12 +1,12 @@
<div class="m-t"> <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"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:40%" translate>{{ 'name' }}</th> <th style="width:40%" translate>{{ 'app.admin.events.name' }}</th>
<th style="width:40%" translate>{{ 'usages_count' }}</th> <th style="width:40%" translate>{{ 'app.admin.events.usages_count' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -17,7 +17,7 @@
<td> <td>
<div class="buttons"> <div class="buttons">
<button class="btn btn-default" ng-click="editPriceCategory(category.id, $index)"> <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>
<button class="btn btn-danger" ng-click="removePriceCategory(category.id, $index)"> <button class="btn btn-danger" ng-click="removePriceCategory(category.id, $index)">
<i class="fa fa-trash-o"></i> <i class="fa fa-trash-o"></i>

View File

@ -7,7 +7,7 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1>{{ 'the_reservations' | translate }} {{event.title}}</h1> <h1>{{ 'app.admin.event_reservations.the_reservations' | translate }} {{event.title}}</h1>
</section> </section>
</div> </div>
</div> </div>
@ -20,35 +20,36 @@
<table class="table" ng-if="reservations.length > 0"> <table class="table" ng-if="reservations.length > 0">
<thead> <thead>
<tr> <tr>
<th style="width:25%" translate>{{ 'user' }}</th> <th style="width:25%" translate>{{ 'app.admin.event_reservations.user' }}</th>
<th style="width:25%" translate>{{ 'payment_date' }}</th> <th style="width:25%" translate>{{ 'app.admin.event_reservations.payment_date' }}</th>
<th style="width:25%" translate>{{ 'reserved_tickets' }}</th> <th style="width:25%" translate>{{ 'app.admin.event_reservations.reserved_tickets' }}</th>
<th style="width:25%"></th> <th style="width:25%"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="reservation in reservations"> <tr ng-repeat="reservation in reservations" ng-class="{'disabled': isCancelled(reservation)}">
<td class="text-c"> <td class="text-c">
<a ui-sref="app.logged.members_show({id: reservation.user_id})">{{ reservation.user_full_name }} </a> <a ui-sref="app.logged.members_show({id: reservation.user_id})">{{ reservation.user_full_name }} </a>
</td> </td>
<td>{{ reservation.created_at | amDateFormat:'LL LTS' }}</td> <td>{{ reservation.created_at | amDateFormat:'LL LTS' }}</td>
<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> <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>
<td> <td>
<div class="buttons"> <div class="buttons">
<button class="btn btn-default" ui-sref="app.public.events_show({id: event.id})"> <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> </button>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </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>
</div> </div>
</section> </section>

View File

@ -1,13 +1,13 @@
<div class="m-t-lg m-b"> <div class="m-t-lg m-b">
<button type="button" class="btn btn-warning" ng-click="addGroup()"> <button type="button" class="btn btn-warning" ng-click="addGroup()">
<i class="fa fa-plus m-r"></i> <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> </button>
<div class="form-group pull-right"> <div class="form-group pull-right">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span> <span class="input-group-addon"><i class="fa fa-filter"></i></span>
<select ng-model="groupFiltering" class="form-control"> <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> </select>
</div> </div>
</div> </div>
@ -16,7 +16,7 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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> <th style="width: 25%"></th>
</tr> </tr>
</thead> </thead>
@ -39,11 +39,11 @@
</form> </form>
<div class="buttons" ng-hide="rowform.$visible || group.slug === 'admins'"> <div class="buttons" ng-hide="rowform.$visible || group.slug === 'admins'">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
<button class="btn btn-default" ng-click="toggleDisableGroup($index)"> <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-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>{{ 'group_form.enable' }}</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>
<button class="btn btn-danger" ng-click="removeGroup($index)"> <button class="btn btn-danger" ng-click="removeGroup($index)">
<i class="fa fa-trash-o"></i> <i class="fa fa-trash-o"></i>

View File

@ -1,10 +1,10 @@
<ul> <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>{{ 'app.admin.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>{{ 'app.admin.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>{{ 'app.admin.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.perpetual_total' }}</span> : <span>{{period.perpetual_total | currency}}</span></li>
<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-link chained" ng-show="period.chained_footprint"></i>
<i class="fa fa-chain-broken broken" ng-hide="period.chained_footprint"></i> <i class="fa fa-chain-broken broken" ng-hide="period.chained_footprint"></i>
</li> </li>

View File

@ -1,11 +1,11 @@
<div class="modal-header"> <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>
<div class="modal-body"> <div class="modal-body">
<form role="form" name="exportForm"> <form role="form" name="exportForm">
<div class="row"> <div class="row">
<div class="form-group col-md-6"> <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"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span> <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" <input type="text"
@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div class="form-group col-md-6"> <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"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span> <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" <input type="text"
@ -42,36 +42,36 @@
</div> </div>
</div> </div>
<div class="row"> <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"> <div class="form-group m-l-lg">
<label for="acd"> <label for="acd">
<input type="radio" name="acd" id="acd" ng-model="exportTarget.software" ng-value="'acd'" ng-click="fillSettings()" required/> <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> </label>
</div> </div>
</div> </div>
<div class="row" ng-show="exportTarget.settings"> <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-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-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-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"> <div class="col-md-8">
<a href="https://apidock.com/ruby/DateTime/strftime" class="help-cursor" target="_blank">{{ exportTarget.settings.dateFormat }}</a> <a href="https://apidock.com/ruby/DateTime/strftime" class="help-cursor" target="_blank">{{ exportTarget.settings.dateFormat }}</a>
</div> </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-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-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-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"> <table class="col-md-12 export-table-template">
<thead> <thead>
<tr> <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> </tr>
</thead> </thead>
<tbody> <tbody>
@ -91,7 +91,7 @@
<input name="type" type="hidden" ng-value="exportTarget.software"/> <input name="type" type="hidden" ng-value="exportTarget.software"/>
<input name="key" type="hidden" ng-value="query.key"/> <input name="key" type="hidden" ng-value="query.key"/>
<input name="query" type="hidden" ng-value="query.query"/> <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> </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> </div>

View File

@ -1,10 +1,10 @@
<div class="modal-header"> <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>
<div class="modal-body"> <div class="modal-body">
<form name="avoirForm" novalidate="novalidate"> <form name="avoirForm" novalidate="novalidate">
<div class="form-group" ng-class="{'has-error': avoirForm.avoir_date.$dirty && avoirForm.avoir_date.$invalid }"> <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"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span> <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" <input type="text"
@ -19,24 +19,24 @@
ng-click="openDatePicker($event)" ng-click="openDatePicker($event)"
required/> required/>
</div> </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>
<div class="form-group"> <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> <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>
<div class="form-group" ng-if="invoice.is_subscription_invoice"> <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> <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>
<div ng-show="!invoice.is_subscription_invoice && invoice.items.length > 1" class="form-group"> <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"> <table class="table partial-avoir-table">
<thead> <thead>
<tr> <tr>
<th class="input-col"></th> <th class="input-col"></th>
<th class="label-col" translate>{{ 'invoices.description' }}</th> <th class="label-col" translate>{{ 'app.admin.invoices.description' }}</th>
<th class="amount-col" translate>{{ 'invoices.price' }}</th> <th class="amount-col" translate>{{ 'app.admin.invoices.price' }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -49,13 +49,13 @@
</table> </table>
</div> </div>
<div> <div>
<label for="description" translate>{{ 'invoices.description_optional' }}</label> <label for="description" translate>{{ 'app.admin.invoices.description_optional' }}</label>
<p translate>{{ 'invoices.will_appear_on_the_refund_invoice' }}</p> <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> <textarea class="form-control m-t-sm" name="description" ng-model="avoir.description"></textarea>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" ng-disabled="avoirForm.$invalid" translate>{{ 'confirm' }}</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>{{ 'cancel' }}</button> <button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div> </div>

View File

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

View File

@ -7,14 +7,14 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'invoices.invoices' }}</h1> <h1 translate>{{ 'app.admin.invoices.invoices' }}</h1>
</section> </section>
</div> </div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md"> <div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
<section class="heading-actions wrapper"> <section class="heading-actions wrapper">
<a class="btn btn-default rounded m-t-sm" ng-click="toggleExportModal()"><i class="fa fa-book"></i></a> <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> <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> </section>
</div> </div>
</div> </div>
@ -24,14 +24,14 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<uib-tabset justified="true"> <uib-tabset justified="true">
<uib-tab heading="{{ 'invoices.invoices_list' | translate }}" ng-hide="fablabWithoutInvoices" active="tabs.listing.active"> <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> {{ 'invoices.filter_invoices' | translate }}</h3> <h3 class="m-t-xs"><i class="fa fa-filter"></i> {{ 'app.admin.invoices.filter_invoices' | translate }}</h3>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<div class="input-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()"> <input type="text" ng-model="searchInvoice.reference" class="form-control" placeholder="" ng-change="handleFilterChange()">
</div> </div>
</div> </div>
@ -40,7 +40,7 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<div class="input-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()"> <input type="text" ng-model="searchInvoice.name" class="form-control" placeholder="" ng-change="handleFilterChange()">
</div> </div>
</div> </div>
@ -49,7 +49,7 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<div class="input-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()"> <input type="date" ng-model="searchInvoice.date" class="form-control" ng-change="handleFilterChange()">
</div> </div>
</div> </div>
@ -64,13 +64,13 @@
<thead> <thead>
<tr> <tr>
<th style="width:5%"></th> <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> <th style="width:30%"></th>
</tr> </tr>
@ -91,13 +91,13 @@
<td> <td>
<div class="buttons"> <div class="buttons">
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="!invoice.is_avoir"> <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>
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="invoice.is_avoir"> <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>
<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)"> <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> </a>
</div> </div>
</td> </td>
@ -105,9 +105,9 @@
</tbody> </tbody>
</table> </table>
<div class="text-center"> <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> </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>
</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"> <div class="alert alert-warning p-md m-t" role="alert" ng-show="fablabWithoutInvoices">
<i class="fa fa-warning m-r"></i> <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> </div>
<form class="invoice-placeholder"> <form class="invoice-placeholder">
<div class="invoice-logo"> <div class="invoice-logo">
@ -128,79 +128,79 @@
<div class="tools-box"> <div class="tools-box">
<div class="btn-group"> <div class="btn-group">
<div class="btn btn-default btn-file"> <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> <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> </div>
</div> </div>
<div class="invoice-buyer-infos"> <div class="invoice-buyer-infos">
<strong translate>{{ 'invoices.john_smith' }}</strong> <strong translate>{{ 'app.admin.invoices.john_smith' }}</strong>
<div translate>{{ 'invoices.john_smith_at_example_com' }}</div> <div translate>{{ 'app.admin.invoices.john_smith_at_example_com' }}</div>
</div> </div>
<div class="invoice-reference invoice-editable" ng-click="openEditReference()">{{ 'invoices.invoice_reference_' | translate }} {{mkReference()}}</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()">{{ 'invoices.code_' | translate }} {{invoice.code.model}}</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>{{ 'invoices.code_disabled' }}</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()"> {{ 'invoices.order_num' | translate }} {{mkNumber()}}</div> <div class="invoice-order invoice-editable" ng-click="openEditInvoiceNb()"> {{ 'app.admin.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-date">{{ 'app.admin.invoices.invoice_issued_on_DATE_at_TIME' | translate:{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT')} }}</div>
<div class="invoice-object"> <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>
<div class="invoice-data"> <div class="invoice-data">
{{ 'invoices.order_summary' | translate }} {{ 'app.admin.invoices.order_summary' | translate }}
<table> <table>
<thead> <thead>
<tr> <tr>
<th translate>{{ 'invoices.details' }}</th> <th translate>{{ 'app.admin.invoices.details' }}</th>
<th class="right" translate>{{ 'invoices.amount' }}</th> <th class="right" translate>{{ 'app.admin.invoices.amount' }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <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> <td class="right">{{30.0 | currency}}</td>
</tr> </tr>
<tr class="invoice-total" ng-class="{'bold vat-line':invoice.VAT.active}"> <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>{{ 'app.admin.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_including_all_taxes' }}</td>
<td class="right">{{30.0 | currency}}</td> <td class="right">{{30.0 | currency}}</td>
</tr> </tr>
<tr class="invoice-vat invoice-activable" ng-click="openEditVAT()" ng-show="!invoice.VAT.active"> <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> <td></td>
</tr> </tr>
<tr class="invoice-vat invoice-editable vat-line italic" ng-click="openEditVAT()" ng-show="invoice.VAT.active"> <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> <td>{{30-(30/(invoice.VAT.rate/100+1)) | currency}}</td>
</tr> </tr>
<tr class="invoice-ht vat-line italic" ng-show="invoice.VAT.active"> <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> <td>{{30/(invoice.VAT.rate/100+1) | currency}}</td>
</tr> </tr>
<tr class="invoice-payed vat-line bold" ng-show="invoice.VAT.active"> <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> <td>{{30.0 | currency}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p class="invoice-payment" translate translate-values="{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT'), AMOUNT:(30.0 | currency)}"> <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> </p>
</div> </div>
<div medium-editor class="invoice-text invoice-editable" ng-model="invoice.text.content" <div medium-editor class="invoice-text invoice-editable" ng-model="invoice.text.content"
options='{ options='{
"placeholder": "{{ "invoices.important_notes" | translate }}", "placeholder": "{{ "app.admin.invoices.important_notes" | translate }}",
"buttons": ["underline"] "buttons": ["underline"]
}' }'
ng-blur="textEditEnd($event)"> ng-blur="textEditEnd($event)">
</div> </div>
<div medium-editor class="invoice-legals invoice-editable" ng-model="invoice.legals.content" <div medium-editor class="invoice-legals invoice-editable" ng-model="invoice.legals.content"
options='{ options='{
"placeholder": "{{ "invoices.address_and_legal_information" | translate }}", "placeholder": "{{ "app.admin.invoices.address_and_legal_information" | translate }}",
"buttons": ["bold", "underline"] "buttons": ["bold", "underline"]
}' }'
ng-blur="legalsEditEnd($event)"> 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 panel-default m-t-md accounting-codes">
<div class="panel-body"> <div class="panel-body">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="journalCode" translate>{{ 'invoices.accounting_journal_code' }}</label> <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="{{ 'invoices.general_journal_code' | translate }}"/> <input type="text" id="journalCode" ng-model="settings.journalCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_journal_code' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="cardClientCode" translate>{{ 'invoices.accounting_card_client_code' }}</label> <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="{{ 'invoices.card_client_code' | translate }}" /> <input type="text" id="cardClientCode" ng-model="settings.cardClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="cardClientLabel" translate>{{ 'invoices.accounting_card_client_label' }}</label> <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="{{ 'invoices.card_client_label' | translate }}"/> <input type="text" id="cardClientLabel" ng-model="settings.cardClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="walletClientCode" translate>{{ 'invoices.accounting_wallet_client_code' }}</label> <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="{{ 'invoices.wallet_client_code' | translate }}" /> <input type="text" id="walletClientCode" ng-model="settings.walletClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="walletClientLabel" translate>{{ 'invoices.accounting_wallet_client_label' }}</label> <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="{{ 'invoices.wallet_client_label' | translate }}"/> <input type="text" id="walletClientLabel" ng-model="settings.walletClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="otherClientCode" translate>{{ 'invoices.accounting_other_client_code' }}</label> <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="{{ 'invoices.other_client_code' | translate }}" /> <input type="text" id="otherClientCode" ng-model="settings.otherClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="otherClientLabel" translate>{{ 'invoices.accounting_other_client_label' }}</label> <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="{{ 'invoices.other_client_label' | translate }}"/> <input type="text" id="otherClientLabel" ng-model="settings.otherClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="walletCode" translate>{{ 'invoices.accounting_wallet_code' }}</label> <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="{{ 'invoices.general_wallet_code' | translate }}" /> <input type="text" id="walletCode" ng-model="settings.walletCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="walletLabel" translate>{{ 'invoices.accounting_wallet_label' }}</label> <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="{{ 'invoices.general_wallet_label' | translate }}"/> <input type="text" id="walletLabel" ng-model="settings.walletLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="vatCode" translate>{{ 'invoices.accounting_vat_code' }}</label> <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="{{ 'invoices.general_vat_code' | translate }}"/> <input type="text" id="vatCode" ng-model="settings.vatCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_code' | translate }}"/>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="vatLabel" translate>{{ 'invoices.accounting_vat_label' }}</label> <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="{{ 'invoices.general_vat_label' | translate }}"/> <input type="text" id="vatLabel" ng-model="settings.vatLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="subscriptionCode" translate>{{ 'invoices.accounting_subscription_code' }}</label> <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="{{ 'invoices.general_subscription_code' | translate }}" /> <input type="text" id="subscriptionCode" ng-model="settings.subscriptionCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="subscriptionLabel" translate>{{ 'invoices.accounting_subscription_label' }}</label> <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="{{ 'invoices.general_subscription_label' | translate }}"/> <input type="text" id="subscriptionLabel" ng-model="settings.subscriptionLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="machineCode" translate>{{ 'invoices.accounting_Machine_code' }}</label> <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="{{ 'invoices.general_machine_code' | translate }}"/> <input type="text" id="machineCode" ng-model="settings.machineCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_code' | translate }}"/>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="machineLabel" translate>{{ 'invoices.accounting_Machine_label' }}</label> <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="{{ 'invoices.general_machine_label' | translate }}"/> <input type="text" id="machineLabel" ng-model="settings.machineLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="trainingCode" translate>{{ 'invoices.accounting_Training_code' }}</label> <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="{{ 'invoices.general_training_code' | translate }}" /> <input type="text" id="trainingCode" ng-model="settings.trainingCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="trainingLabel" translate>{{ 'invoices.accounting_Training_label' }}</label> <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="{{ 'invoices.general_training_label' | translate }}"/> <input type="text" id="trainingLabel" ng-model="settings.trainingLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="eventCode" translate>{{ 'invoices.accounting_Event_code' }}</label> <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="{{ 'invoices.general_event_code' | translate }}"/> <input type="text" id="eventCode" ng-model="settings.eventCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_code' | translate }}"/>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="eventLabel" translate>{{ 'invoices.accounting_Event_label' }}</label> <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="{{ 'invoices.general_event_label' | translate }}"/> <input type="text" id="eventLabel" ng-model="settings.eventLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_label' | translate }}"/>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label for="spaceCode" translate>{{ 'invoices.accounting_Space_code' }}</label> <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="{{ 'invoices.general_space_code' | translate }}" /> <input type="text" id="spaceCode" ng-model="settings.spaceCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_code' | translate }}" />
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="spaceLabel" translate>{{ 'invoices.accounting_Space_label' }}</label> <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="{{ 'invoices.general_space_label' | translate }}"/> <input type="text" id="spaceLabel" ng-model="settings.spaceLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_label' | translate }}"/>
</div> </div>
</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>
</div> </div>
</uib-tab> </uib-tab>
@ -333,125 +333,125 @@
<script type="text/ng-template" id="editReference.html"> <script type="text/ng-template" id="editReference.html">
<div class="custom-invoice"> <div class="custom-invoice">
<div class="modal-header"> <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>
<div class="modal-body row"> <div class="modal-body row">
<div class="elements col-md-4"> <div class="elements col-md-4">
<h4>Éléments</h4> <h4>Éléments</h4>
<ul> <ul>
<li ng-click="invoice.reference.help = 'addYear.html'">{{ 'invoices.year' | translate }}</li> <li ng-click="invoice.reference.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
<li ng-click="invoice.reference.help = 'addMonth.html'">{{ 'invoices.month' | translate }}</li> <li ng-click="invoice.reference.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
<li ng-click="invoice.reference.help = 'addDay.html'">{{ 'invoices.day' | translate }}</li> <li ng-click="invoice.reference.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
<li ng-click="invoice.reference.help = 'addInvoiceNumber.html'">{{ 'invoices.num_of_invoice' | 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'">{{ 'invoices.online_sales' | 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'">{{ 'invoices.wallet' | translate }}</li> %> <%# <li ng-click="invoice.reference.help = 'addWalletInfo.html'">{{ 'app.admin.invoices.wallet' | translate }}</li> %>
<li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'invoices.refund' | translate }}</li> <li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'app.admin.invoices.refund' | translate }}</li>
</ul> </ul>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div class="model"> <div class="model">
<h4 translate>{{ 'invoices.model' }}</h4> <h4 translate>{{ 'app.admin.invoices.model' }}</h4>
<input type="text" class="form-control" ng-model="model"> <input type="text" class="form-control" ng-model="model">
</div> </div>
<div class="help"> <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 src="invoice.reference.help" autoscroll="true">
</ng-include> </ng-include>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'confirm' }}</button> <button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<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> </div>
</div> </div>
</script> </script>
<script type="text/ng-template" id="addYear.html"> <script type="text/ng-template" id="addYear.html">
<table class="invoice-element-legend"> <table class="invoice-element-legend">
<tr><td><strong>YY</strong></td><td translate>{{ 'invoices.2_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>{{ 'invoices.4_digits_year' }}</td></tr> <tr><td><strong>YYYY</strong></td><td translate>{{ 'app.admin.invoices.4_digits_year' }}</td></tr>
</table> </table>
</script> </script>
<script type="text/ng-template" id="addMonth.html"> <script type="text/ng-template" id="addMonth.html">
<table class="invoice-element-legend"> <table class="invoice-element-legend">
<tr><td><strong>M</strong></td><td translate>{{ 'invoices.month_number' }}</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>{{ 'invoices.2_digits_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>{{ 'invoices.3_characters_month_name' }}</td></tr> <tr><td><strong>MMM</strong></td><td translate>{{ 'app.admin.invoices.3_characters_month_name' }}</td></tr>
</table> </table>
</script> </script>
<script type="text/ng-template" id="addDay.html"> <script type="text/ng-template" id="addDay.html">
<table class="invoice-element-legend"> <table class="invoice-element-legend">
<tr><td><strong>D</strong></td><td translate>{{ 'invoices.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>{{ 'invoices.2_digits_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> </table>
</script> </script>
<script type="text/ng-template" id="addInvoiceNumber.html"> <script type="text/ng-template" id="addInvoiceNumber.html">
<table class="invoice-element-legend"> <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>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>{{ 'invoices.n_digits_monthly_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>{{ 'invoices.n_digits_annual_amount_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> </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>
<script type="text/ng-template" id="addOrderNumber.html"> <script type="text/ng-template" id="addOrderNumber.html">
<table class="invoice-element-legend"> <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>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>{{ 'invoices.n_digits_daily_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>{{ 'invoices.n_digits_monthly_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>{{ 'invoices.n_digits_annual_amount_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> </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>
<script type="text/ng-template" id="addOnlineInfo.html"> <script type="text/ng-template" id="addOnlineInfo.html">
<table class="invoice-element-legend"> <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> </table>
</script> </script>
<script type="text/ng-template" id="addWalletInfo.html"> <script type="text/ng-template" id="addWalletInfo.html">
<table class="invoice-element-legend"> <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> </table>
</script> </script>
<script type="text/ng-template" id="addRefundInfo.html"> <script type="text/ng-template" id="addRefundInfo.html">
<table class="invoice-element-legend"> <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> </table>
</script> </script>
<script type="text/ng-template" id="editCode.html"> <script type="text/ng-template" id="editCode.html">
<div class="custom-invoice"> <div class="custom-invoice">
<div class="modal-header"> <div class="modal-header">
<h3 class="modal-title" translate>{{ 'invoices.code' }}</h3> <h3 class="modal-title" translate>{{ 'app.admin.invoices.code' }}</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <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 <input bs-switch
ng-model="isSelected" ng-model="isSelected"
id="enableCode" id="enableCode"
type="checkbox" type="checkbox"
class="form-control m-l-sm" class="form-control m-l-sm"
switch-on-text="{{ 'invoices.enabled' | translate }}" switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
switch-off-text="{{ 'invoices.disabled' | translate }}" switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
switch-animate="true"/> switch-animate="true"/>
</div> </div>
<div class="form-group" ng-show="isSelected"> <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"/> <input id="codeModel" type="text" ng-model="codeModel" class="form-control"/>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'confirm' }}</button> <button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<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> </div>
</div> </div>
</script> </script>
@ -461,33 +461,33 @@
<script type="text/ng-template" id="editNumber.html"> <script type="text/ng-template" id="editNumber.html">
<div class="custom-invoice"> <div class="custom-invoice">
<div class="modal-header"> <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>
<div class="modal-body row"> <div class="modal-body row">
<div class="elements col-md-4"> <div class="elements col-md-4">
<h4 translate>{{ 'invoices.elements' }}</h4> <h4 translate>{{ 'app.admin.invoices.elements' }}</h4>
<ul> <ul>
<li ng-click="invoice.number.help = 'addYear.html'">{{ 'invoices.year' | translate }}</li> <li ng-click="invoice.number.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
<li ng-click="invoice.number.help = 'addMonth.html'">{{ 'invoices.month' | translate }}</li> <li ng-click="invoice.number.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
<li ng-click="invoice.number.help = 'addDay.html'">{{ 'invoices.day' | translate }}</li> <li ng-click="invoice.number.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
<li ng-click="invoice.number.help = 'addOrderNumber.html'">{{ 'invoices.order_num' | translate }}</li> <li ng-click="invoice.number.help = 'addOrderNumber.html'">{{ 'app.admin.invoices.order_num' | translate }}</li>
</ul> </ul>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div class="model"> <div class="model">
<h4 translate>{{ 'invoices.model' }}</h4> <h4 translate>{{ 'app.admin.invoices.model' }}</h4>
<input type="text" class="form-control" ng-model="model"> <input type="text" class="form-control" ng-model="model">
</div> </div>
<div class="help"> <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 src="invoice.number.help" autoscroll="true">
</ng-include> </ng-include>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'confirm' }}</button> <button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<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> </div>
</div> </div>
</script> </script>
@ -496,23 +496,23 @@
<script type="text/ng-template" id="editVAT.html"> <script type="text/ng-template" id="editVAT.html">
<div class="custom-invoice"> <div class="custom-invoice">
<div class="modal-header"> <div class="modal-header">
<h3 class="modal-title" translate>{{ 'invoices.VAT' }}</h3> <h3 class="modal-title" translate>{{ 'app.admin.invoices.VAT' }}</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <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 <input bs-switch
ng-model="isSelected" ng-model="isSelected"
id="enableVAT" id="enableVAT"
type="checkbox" type="checkbox"
class="form-control m-l-sm" class="form-control m-l-sm"
switch-on-text="{{ 'invoices.enabled' | translate }}" switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
switch-off-text="{{ 'invoices.disabled' | translate }}" switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
switch-animate="true"/> switch-animate="true"/>
</div> </div>
<div class="form-group" ng-show="isSelected"> <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"> <div class="input-group">
<span class="input-group-addon">% </span> <span class="input-group-addon">% </span>
<input id="vatRate" type="number" ng-model="rate" class="form-control" min="0" max="100"/> <input id="vatRate" type="number" ng-model="rate" class="form-control" min="0" max="100"/>
@ -520,32 +520,32 @@
</div> </div>
<div class="m-t-lg"> <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"> <table class="table scrollable-3-cols">
<thead> <thead>
<tr> <tr>
<th translate>{{ 'invoices.VAT_rate' }}</th> <th translate>{{ 'app.admin.invoices.VAT_rate' }}</th>
<th translate>{{ 'invoices.changed_at' }}</th> <th translate>{{ 'app.admin.invoices.changed_at' }}</th>
<th translate>{{ 'invoices.changed_by' }}</th> <th translate>{{ 'app.admin.invoices.changed_by' }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="value in history | orderBy:'-date'"> <tr ng-repeat="value in history | orderBy:'-date'">
<td> <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 === false" translate>{{'app.admin.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 === true" translate>{{'app.admin.invoices.VAT_enabled'}}</span>
<span ng-show="value.rate">{{value.rate}}</span> <span ng-show="value.rate">{{value.rate}}</span>
</td> </td>
<td>{{value.date | amDateFormat:'L LT'}}</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> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'confirm' }}</button> <button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
<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> </div>
</div> </div>
</script> </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}"> <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"> <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> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span>
</label> </label>
<div class="col-sm-9"> <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 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> </select>
<input type="hidden" name="user[group_id]" ng-value="user.group_id" /> <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> </div>
<div class="form-group"> <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"> <div class="col-sm-10">
<input type="hidden" name="user[statistic_profile_attributes][training_ids][]" value="" /> <input type="hidden" name="user[statistic_profile_attributes][training_ids][]" value="" />
<ui-select multiple ng-model="user.training_ids" class="form-control"> <ui-select multiple ng-model="user.training_ids" class="form-control">
@ -28,7 +28,7 @@
</div> </div>
<div class="form-group"> <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"> <div class="col-sm-10">
<input type="hidden" name="user[tag_ids][]" value="" /> <input type="hidden" name="user[tag_ids][]" value="" />
<ui-select multiple ng-model="user.tag_ids" name="user[tag_ids][]" class="form-control"> <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="form-group">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span> <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>
</div> </div>
<div class="col-md-12"> <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"> <table class="table">
<thead> <thead>
<tr> <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> <th style="width:10%"></th>
</tr> </tr>
</thead> </thead>

View File

@ -9,8 +9,8 @@
</div> </div>
<div class="col-md-8 b-l b-r"> <div class="col-md-8 b-l b-r">
<section class="heading-title"> <section class="heading-title">
<h1 class="inline">{{ 'user' | translate }} {{ user.name }}</h1> <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>{{ 'incomplete_profile' }}</span> <span class="label label-danger text-white" ng-show="user.need_completion" translate>{{ 'app.shared.user_admin.incomplete_profile' }}</span>
</section> </section>
</div> </div>
@ -18,7 +18,7 @@
<div class="col-md-3"> <div class="col-md-3">
<section class="heading-actions wrapper"> <section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate> <div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }} {{ 'app.shared.buttons.cancel' }}
</div> </div>
</section> </section>
@ -34,11 +34,11 @@
<uib-tabset justified="true" class="m-t"> <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'"> <section class="panel panel-danger m-lg" ng-show="user.need_completion && activeProvider.providable_type !== 'DatabaseProvider'">
<div class="panel-body m-r" translate> <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> </div>
</section> </section>
@ -46,21 +46,21 @@
<section class="panel panel-default bg-light m-lg"> <section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r"> <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> <!-- ./panel-body -->
<div class="panel-footer no-padder"> <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> </div>
</section> </section>
</form> </form>
</uib-tab> </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"> <section class="panel panel-default bg-light m-lg">
@ -68,24 +68,24 @@
<div class="" ng-show="subscription"> <div class="" ng-show="subscription">
<h3>{{ subscription.plan | humanReadablePlanName }}</h3> <h3>{{ subscription.plan | humanReadablePlanName }}</h3>
<p> <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>
<p> <p>
{{ 'expires_at' | translate }} {{ subscription.expired_at | amDateFormat: 'L' }} {{ 'app.admin.members_edit.expires_at' | translate }} {{ subscription.expired_at | amDateFormat: 'L' }}
</p> </p>
<p> <p>
{{ 'price_' | translate }} {{ subscription.plan.amount | currency}} {{ 'app.admin.members_edit.price_' | translate }} {{ subscription.plan.amount | currency}}
</p> </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, true)" translate>{{ 'app.admin.members_edit.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, false)" translate>{{ 'app.admin.members_edit.extend_subscription' }}</button>
</div> </div>
<div class="" ng-hide="subscription"> <div class="" ng-hide="subscription">
<p translate> <p translate>
{{ 'user_has_no_current_subscription' }} {{ 'app.admin.members_edit.user_has_no_current_subscription' }}
</p> </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>
</div> </div>
@ -93,11 +93,11 @@
</section> </section>
</uib-tab> </uib-tab>
<uib-tab heading="{{ 'trainings' | translate }}"> <uib-tab heading="{{ 'app.admin.members_edit.trainings' | translate }}">
<div class="col-md-4"> <div class="col-md-4">
<div class="widget panel b-a m-t-lg"> <div class="widget panel b-a m-t-lg">
<div class="panel-heading b-b "> <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>
<div class="widget-content bg-light wrapper r-b"> <div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.training_reservations.length > 0"> <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> <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> </li>
</ul> </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>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="widget panel b-a m-t-lg"> <div class="widget panel b-a m-t-lg">
<div class="panel-heading b-b"> <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>
<div class="widget-content bg-light wrapper r-b"> <div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.training_reservations.length > 0"> <ul class="list-unstyled" ng-if="user.training_reservations.length > 0">
@ -125,14 +125,14 @@
</div> --> </div> -->
</li> </li>
</ul> </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>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="widget panel b-a m-t-lg"> <div class="widget panel b-a m-t-lg">
<div class="panel-heading b-b"> <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>
<div class="widget-content bg-light wrapper r-b"> <div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.trainings.length > 0"> <ul class="list-unstyled" ng-if="user.trainings.length > 0">
@ -140,17 +140,17 @@
<span class="font-sbold">{{t.name}}</span> <span class="font-sbold">{{t.name}}</span>
</li> </li>
</ul> </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> </div>
</div> </div>
</uib-tab> </uib-tab>
<uib-tab heading="{{ 'events' | translate }}"> <uib-tab heading="{{ 'app.admin.members_edit.events' | translate }}">
<div class="col-md-6"> <div class="col-md-6">
<div class="widget panel b-a m m-t-lg"> <div class="widget panel b-a m m-t-lg">
<div class="panel-heading b-b"> <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>
<div class="widget-content bg-light wrapper r-b"> <div class="widget-content bg-light wrapper r-b">
<ul class="list-unstyled" ng-if="user.events_reservations.length > 0"> <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> <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"> <span ng-if="r.nb_reserve_places > 0">
<br/> <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>
<span ng-repeat="ticket in r.tickets"> <span ng-repeat="ticket in r.tickets">
<br/> <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> </span>
</li> </li>
</ul> </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>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="widget panel b-a m m-t-lg"> <div class="widget panel b-a m m-t-lg">
<div class="panel-heading b-b"> <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>
<div class="widget-content bg-light auto wrapper r-b"> <div class="widget-content bg-light auto wrapper r-b">
<ul class="list-unstyled" ng-if="user.events_reservations.length > 0"> <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> <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> </li>
</ul> </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> </div>
</div> </div>
</uib-tab> </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"> <div class="col-md-12 m m-t-lg">
<table class="table" ng-if="user.invoices.length > 0"> <table class="table" ng-if="user.invoices.length > 0">
<thead> <thead>
<tr> <tr>
<th style="width:25%" translate>{{ 'invoice_num' }}</th> <th style="width:25%" translate>{{ 'app.admin.members_edit.invoice_num' }}</th>
<th style="width:25%" translate>{{ 'date' }}</th> <th style="width:25%" translate>{{ 'app.admin.members_edit.date' }}</th>
<th style="width:25%" translate>{{ 'price' }}</th> <th style="width:25%" translate>{{ 'app.admin.members_edit.price' }}</th>
<th style="width:25%"></th> <th style="width:25%"></th>
</tr> </tr>
</thead> </thead>
@ -209,34 +209,34 @@
<td> <td>
<div class="buttons"> <div class="buttons">
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="!invoice.is_avoir"> <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>
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="invoice.is_avoir"> <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> </a>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </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> </div>
</uib-tab> </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"> <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="clearfix"></div>
<div class="col-sm-4 text-center"> <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> </div>
<div class="col-md-12 m m-t-lg"> <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> </div>
</uib-tab> </uib-tab>
</uib-tabset> </uib-tabset>

View File

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

View File

@ -11,7 +11,7 @@
<div class="col-md-8 b-l"> <div class="col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'members_import_result.import_results' }}</h1> <h1 translate>{{ 'app.admin.members_import_result.import_results' }}</h1>
</section> </section>
</div> </div>
@ -22,11 +22,11 @@
<div class="row no-gutter"> <div class="row no-gutter">
<div class="col-sm-12 col-md-12"> <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"> <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="row p-h-lg" ng-repeat="resultRow in results track by $index">
<div class="scroll-x"> <div class="scroll-x">
@ -41,21 +41,21 @@
</div> </div>
<div ng-if="resultRow.status"> <div ng-if="resultRow.status">
<i class="fa fa-arrow-right m-l-lg m-r"></i> <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"> <span ng-show="resultRow.result" class="green font-bold">
<i class="fa fa-check-square-o fa-stack-outside"></i> <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>
<span ng-hide="resultRow.result" class="text-red-only font-bold"> <span ng-hide="resultRow.result" class="text-red-only font-bold">
<span class="fa-stack v-bottom"> <span class="fa-stack v-bottom">
<i class="fa fa-square-o fa-stack-1x fa-stack-outside"></i> <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> <i class="fa fa-times fa-stack-1x fa-stack-inside"></i>
</span> </span>
<span class="m-l" translate>{{ 'members_import_result.failed' }}</span> <span class="m-l" translate>{{ 'app.admin.members_import_result.failed' }}</span>
</span> </span>
</div> </div>
<div class="m-l-lg text-red-only" ng-if="!resultRow.row && !resultRow.status"> <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> </div>
</div> </div>

View File

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

View File

@ -2,21 +2,21 @@
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span> <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>
</div> </div>
<div class="col-md-12"> <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"> <div class="pull-right">
<a class="btn btn-default" ng-href="api/members/export_members.xlsx" target="export-frame" ng-click="alertExport('members')"> <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>
<a class="btn btn-default" ng-href="api/members/export_subscriptions.xlsx" target="export-frame" ng-if="!fablabWithoutPlans" ng-click="alertExport('subscriptions')"> <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>
<a class="btn btn-default" ng-href="api/members/export_reservations.xlsx" target="export-frame" ng-click="alertExport('reservations')"> <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> </a>
<iframe name="export-frame" height="0" width="0" class="none"></iframe> <iframe name="export-frame" height="0" width="0" class="none"></iframe>
</div> </div>
@ -25,12 +25,12 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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('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')">{{ '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%"><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')">{{ '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: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')">{{ '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: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')">{{ '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: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')">{{ '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%" 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> <th style="width:10%"></th>
</tr> </tr>
</thead> </thead>
@ -45,15 +45,18 @@
<td> <td>
<div class="buttons"> <div class="buttons">
<button class="btn btn-default" ui-sref="app.admin.members_edit({id: m.id})"> <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> </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> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="text-center"> <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>
</div> </div>

View File

@ -9,7 +9,7 @@
</div> </div>
<div class="col-md-8 b-l b-r"> <div class="col-md-8 b-l b-r">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'members_new.add_a_member' }}</h1> <h1 translate>{{ 'app.admin.members_new.add_a_member' }}</h1>
</section> </section>
</div> </div>
@ -17,7 +17,7 @@
<div class="col-md-3"> <div class="col-md-3">
<section class="heading-actions wrapper"> <section class="heading-actions wrapper">
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate> <div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" translate>
{{ 'cancel' }} {{ 'app.shared.buttons.cancel' }}
</div> </div>
</section> </section>
@ -45,19 +45,19 @@
ng-model="user.organization" ng-model="user.organization"
ng-change="toggleOrganization()" ng-change="toggleOrganization()"
value="false"/> 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> </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> <!-- ./panel-body -->
<div class="panel-footer no-padder"> <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> </div>
</section> </section>
</form> </form>

View File

@ -7,7 +7,7 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-7 b-l b-r-md"> <div class="col-xs-10 col-sm-10 col-md-7 b-l b-r-md">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'open_api_clients' }}</h1> <h1 translate>{{ 'app.admin.open_api_clients.open_api_clients' }}</h1>
</section> </section>
</div> </div>
@ -15,7 +15,7 @@
<section class="heading-actions wrapper"> <section class="heading-actions wrapper">
<a href="<%= apipie_apipie_path({version: 'v1'}) %>" target="_blank" class="btn btn-info b-2x rounded m-t-sm"> <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; <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> <span class="exponent"><i class="fa fa-external-link" aria-hidden="true"></i></span>
</a> </a>
</section> </section>
@ -28,27 +28,27 @@
<div class="col-md-12"> <div class="col-md-12">
<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> <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}"> <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> </div>
<button class="btn btn-default" ng-click="toggleForm()" name="button">{{ 'cancel' | translate }}</button> <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="{{ 'save' | translate }}"> <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> </form>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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> <th style="width:25%"></th>
</tr> </tr>
@ -62,11 +62,11 @@
<td> <td>
<div class="buttons"> <div class="buttons">
<button class="btn btn-default" ng-click="editClient(client)"> <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>
<button class="btn btn-default" ng-click="resetToken(client)"> <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>
<button class="btn btn-danger" ng-click="deleteClient($index)" ng-show="client.calls_count == 0"> <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}}"> <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}"> <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]" <input type="text" id="plan[base_name]"
name="plan[base_name]" name="plan[base_name]"
class="form-control" class="form-control"
ng-maxlength="24" ng-maxlength="24"
ng-model="plan.base_name" ng-model="plan.base_name"
required="required"/> 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.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>{{ '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.maxlength" translate>{{ 'app.shared.plan.name_length_must_be_less_than_24_characters' }}</span>
</div> </div>
<div class="form-group" ng-class="{'has-error': planForm['plan[type]'].$dirty && planForm['plan[type]'].$invalid}"> <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]" <select id="plan[type]"
name="plan[type]" name="plan[type]"
class="form-control" class="form-control"
ng-model="plan.type" ng-model="plan.type"
required="required" required="required"
ng-disabled="method == 'PATCH'"> ng-disabled="method == 'PATCH'">
<option value="Plan" ng-selected="plan.type == 'Plan'" translate>{{ 'standard' }}</option> <option value="Plan" ng-selected="plan.type == 'Plan'" translate>{{ 'app.shared.plan.standard' }}</option>
<option value="PartnerPlan" ng-selected="plan.type == 'PartnerPlan'" translate>{{ 'partner' }}</option> <option value="PartnerPlan" ng-selected="plan.type == 'PartnerPlan'" translate>{{ 'app.shared.plan.partner' }}</option>
</select> </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>
<div class="form-group" ng-class="{'has-error': planForm['plan[group_id]'].$dirty && planForm['plan[group_id]'].$invalid}"> <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]" <select id="plan[group_id]"
name="plan[group_id]" name="plan[group_id]"
class="form-control" class="form-control"
ng-model="plan.group_id" ng-model="plan.group_id"
required="required" required="required"
ng-disabled="method == 'PATCH'"> 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"> <optgroup label="Groupes">
<option ng-repeat="group in groups" value="{{group.id}}" ng-selected="plan.group_id == group.id">{{group.name}}</option> <option ng-repeat="group in groups" value="{{group.id}}" ng-selected="plan.group_id == group.id">{{group.name}}</option>
</optgroup> </optgroup>
</select> </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>
<div class="form-group" ng-class="{'has-error': planForm['plan[interval]'].$dirty && planForm['plan[interval]'].$invalid}"> <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]" <select id="plan[interval]"
name="plan[interval]" name="plan[interval]"
class="form-control" class="form-control"
ng-model="plan.interval" ng-model="plan.interval"
ng-disabled="method == 'PATCH'" ng-disabled="method == 'PATCH'"
required="required"> required="required">
<option value="week" ng-selected="plan.interval == 'week'" translate>{{ 'plan_form.week' }}</option> <option value="week" ng-selected="plan.interval == 'week'" translate>{{ 'app.shared.plan.week' }}</option>
<option value="month" ng-selected="plan.interval == 'month'" translate>{{ 'plan_form.month' }}</option> <option value="month" ng-selected="plan.interval == 'month'" translate>{{ 'app.shared.plan.month' }}</option>
<option value="year" ng-selected="plan.interval == 'year'" translate>{{ 'plan_form.year' }}</option> <option value="year" ng-selected="plan.interval == 'year'" translate>{{ 'app.shared.plan.year' }}</option>
</select> </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>
<div class="form-group" ng-class="{'has-error': planForm['plan[interval_count]'].$dirty && planForm['plan[interval_count]'].$invalid}"> <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]" <input id="plan[interval_count]"
name="plan[interval_count]" name="plan[interval_count]"
class="form-control" class="form-control"
@ -66,12 +66,12 @@
ng-disabled="method == 'PATCH'" ng-disabled="method == 'PATCH'"
required="required" required="required"
min="1"/> 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>
<div class="form-group"> <div class="form-group">
<div class="input-group" ng-class="{'has-error': planForm['plan[amount]'].$dirty && planForm['plan[amount]'].$invalid}"> <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"> <div class="input-group">
<span class="input-group-addon">{{currencySymbol}}</span> <span class="input-group-addon">{{currencySymbol}}</span>
<input id="plan[amount]" <input id="plan[amount]"
@ -81,40 +81,40 @@
ng-required="true" ng-required="true"
ng-model="plan.amount"/> ng-model="plan.amount"/>
</div> </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> </div>
<div class="form-group"> <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" <input ng-model="plan.ui_weight"
type="number" type="number"
name="plan[ui_weight]" name="plan[ui_weight]"
class="form-control"> class="form-control">
<span class="help-block"> <span class="help-block">
{{ 'plan_form.on_the_subscriptions_page_the_most_prominent_subscriptions_will_be_placed_at_the_top_of_the_list' | translate }} {{ 'app.shared.plan.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.an_evelated_number_means_a_higher_prominence' | translate }}
</span> </span>
</div> </div>
<div class="input-group m-t-md"> <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 <input bs-switch
ng-model="plan.is_rolling" ng-model="plan.is_rolling"
id="plan[is_rolling]" id="plan[is_rolling]"
ng-if="method != 'PATCH'" ng-if="method != 'PATCH'"
type="checkbox" type="checkbox"
class="form-control" class="form-control"
switch-on-text="{{ 'yes' | translate }}" switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'no' | translate }}" switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true" switch-animate="true"
ng-true-value="'true'" ng-true-value="'true'"
ng-false-value="'false'"/> ng-false-value="'false'"/>
<span ng-if="method == 'PATCH'">{{ (plan.is_rolling ? 'yes' : 'no') | translate }}</span> <span ng-if="method == 'PATCH'">{{ (plan.is_rolling ? 'yes' : 'no') | translate }}</span>
<input type="hidden" name="plan[is_rolling]" value="{{plan.is_rolling}}"/> <input type="hidden" name="plan[is_rolling]" value="{{plan.is_rolling}}"/>
<span class="help-block"> <span class="help-block">
{{ 'plan_form.a_rolling_subscription_will_begin_the_day_of_the_first_training' | translate }} {{ 'app.shared.plan.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.otherwise_it_will_begin_as_soon_as_it_is_bought' | translate }}
</span> </span>
</div> </div>
@ -122,13 +122,13 @@
<!-- PDF description attachement --> <!-- 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.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"/> <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="fileinput input-group" data-provides="fileinput" ng-class="fileinputClass(plan.plan_file_attributes)">
<div class="form-control" data-trigger="fileinput"> <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> <i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">{{file.attachment || plan.plan_file_attributes.attachment_identifier}}</span>
</div> </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="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>{{ 'change' }}</span><input type="file" <span class="fileinput-exists" translate>{{ 'app.shared.buttons.change' }}</span><input type="file"
name="plan[plan_file_attributes][attachment]" name="plan[plan_file_attributes][attachment]"
accept="image/*, application/pdf"></span> 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> <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'"> <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" /> <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"> <div class="input-group">
<select class="form-control" <select class="form-control"
ng-model="plan.partnerId" ng-model="plan.partnerId"
@ -145,10 +145,10 @@
<option value=""></option> <option value=""></option>
</select> </select>
<span class="input-group-btn"> <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> </span>
</div> </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>
<div class="form-group" ng-show="plan.partners"> <div class="form-group" ng-show="plan.partners">

View File

@ -7,13 +7,13 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <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> </section>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<section class="heading-actions wrapper"> <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> </section>
</div> </div>
@ -28,37 +28,37 @@
<div id="planForm"> <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"> <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"> <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 <input bs-switch
ng-model="plan.disabled" ng-model="plan.disabled"
id="plan[disabled]" id="plan[disabled]"
type="checkbox" type="checkbox"
class="form-control" class="form-control"
switch-on-text="{{ 'yes' | translate }}" switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
switch-off-text="{{ 'no' | translate }}" switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
switch-animate="true" switch-animate="true"
ng-true-value="'true'" ng-true-value="'true'"
ng-false-value="'false'"/> ng-false-value="'false'"/>
<input type="hidden" name="plan[disabled]" value="{{plan.disabled}}"/> <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> </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"> <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"/> <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"> <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> <option value=""></option>
</select> </select>
</div> </div>
<h3 translate>{{ 'edit_plan.machines' }}</h3> <h3 translate>{{ 'app.admin.plans.edit.machines' }}</h3>
<table class="table"> <table class="table">
<thead> <thead>
<th translate>{{ 'edit_plan.machine' }}</th> <th translate>{{ 'app.admin.plans.edit.machine' }}</th>
<th translate>{{ 'edit_plan.hourly_rate' }}</th> <th translate>{{ 'app.admin.plans.edit.hourly_rate' }}</th>
</thead> </thead>
<tbody> <tbody>
@ -75,11 +75,11 @@
</tbody> </tbody>
</table> </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"> <table class="table" ng-hide="fablabWithoutSpaces">
<thead> <thead>
<th translate>{{ 'edit_plan.space' }}</th> <th translate>{{ 'app.admin.plans.edit.space' }}</th>
<th translate>{{ 'edit_plan.hourly_rate' }}</th> <th translate>{{ 'app.admin.plans.edit.hourly_rate' }}</th>
</thead> </thead>
<tbody> <tbody>
@ -97,7 +97,7 @@
</table> </table>
<div class="panel-footer no-padder"> <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> </div>
</form> </form>
</div> </div>

View File

@ -7,7 +7,7 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <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> </section>
</div> </div>
@ -20,10 +20,10 @@
<div id="planForm"> <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"> <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"> <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> </div>
</form> </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"> <div class="m-t-lg m-b">
<button type="button" class="btn btn-warning" ui-sref="app.admin.coupons_new"> <button type="button" class="btn btn-warning" ui-sref="app.admin.coupons_new">
<i class="fa fa-plus m-r"></i> <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> </button>
<div class="form-group pull-right"> <div class="form-group pull-right">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span> <span class="input-group-addon"><i class="fa fa-filter"></i></span>
<select ng-model="filter.coupon" class="form-control" ng-change="updateCouponFilter()"> <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> </select>
</div> </div>
</div> </div>
@ -18,10 +18,10 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th translate>{{ 'pricing.name' }}</th> <th translate>{{ 'app.admin.pricing.name' }}</th>
<th translate>{{ 'pricing.discount' }}</th> <th translate>{{ 'app.admin.pricing.discount' }}</th>
<th translate>{{ 'pricing.nb_of_usages' }}</th> <th translate>{{ 'app.admin.pricing.nb_of_usages' }}</th>
<th translate>{{ 'pricing.status' }}</th> <th translate>{{ 'app.admin.pricing.status' }}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -33,7 +33,7 @@
<span ng-show="coupon.type == 'amount_off'">{{coupon.amount_off}} {{currencySymbol}}</span> <span ng-show="coupon.type == 'amount_off'">{{coupon.amount_off}} {{currencySymbol}}</span>
</td> </td>
<td>{{coupon.usages}}</td> <td>{{coupon.usages}}</td>
<td translate>{{'pricing.'+coupon.status}}</td> <td translate>{{'app.admin.pricing.'+coupon.status}}</td>
<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" 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> <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> </table>
<div class="text-center"> <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> </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"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:20%" translate>{{ 'pricing.subscription' }}</th> <th style="width:20%" translate>{{ 'app.admin.pricing.subscription' }}</th>
<th style="width:10%" translate>{{ 'pricing.credits' }}</th> <th style="width:10%" translate>{{ 'app.admin.pricing.credits' }}</th>
<th style="width:50%" translate>{{ 'pricing.related_trainings' }}</th> <th style="width:50%" translate>{{ 'app.admin.pricing.related_trainings' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -35,7 +35,7 @@
</form> </form>
<div class="buttons" ng-show="!rowform.$visible"> <div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
</div> </div>
</td> </td>
@ -43,17 +43,17 @@
</tbody> </tbody>
</table> </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"> <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> </div>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:20%" translate>{{ 'pricing.machine' }}</th> <th style="width:20%" translate>{{ 'app.admin.pricing.machine' }}</th>
<th style="width:10%" translate>{{ 'pricing.hours' }}</th> <th style="width:10%">{{ 'app.admin.pricing.hours' | translate:{DURATION:slotDuration} }}</th>
<th style="width:50%" translate>{{ 'pricing.related_subscriptions' }}</th> <th style="width:50%" translate>{{ 'app.admin.pricing.related_subscriptions' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -85,10 +85,10 @@
</form> </form>
<div class="buttons" ng-show="!rowform.$visible"> <div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
<button class="btn btn-danger" ng-click="removeMachineCredit($index)"> <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> </button>
</div> </div>
</td> </td>
@ -96,16 +96,16 @@
</tbody> </tbody>
</table> </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"> <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> </div>
<table ng-hide="fablabWithoutSpaces" class="table"> <table ng-hide="fablabWithoutSpaces" class="table">
<thead> <thead>
<tr> <tr>
<th style="width:20%" translate>{{ 'pricing.space' }}</th> <th style="width:20%" translate>{{ 'app.admin.pricing.space' }}</th>
<th style="width:10%" translate>{{ 'pricing.hours' }}</th> <th style="width:10%">{{ 'app.admin.pricing.hours' | translate:{DURATION:slotDuration} }}</th>
<th style="width:50%" translate>{{ 'pricing.related_subscriptions' }}</th> <th style="width:50%" translate>{{ 'app.admin.pricing.related_subscriptions' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -137,10 +137,10 @@
</form> </form>
<div class="buttons" ng-show="!rowform.$visible"> <div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
<button class="btn btn-danger" ng-click="removeSpaceCredit($index)"> <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> </button>
</div> </div>
</td> </td>

View File

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

View File

@ -1,10 +1,10 @@
<div class="alert alert-warning m-t"> <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> </div>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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"> <th style="width:20%" ng-repeat="group in enabledGroups">
<span class="text-u-c text-sm">{{group.name}}</span> <span class="text-u-c text-sm">{{group.name}}</span>
</th> </th>

View File

@ -1,11 +1,11 @@
<div class="modal-header"> <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>
<div class="modal-body"> <div class="modal-body">
<select-member></select-member> <select-member></select-member>
<div class="widget panel b-a m"> <div class="widget panel b-a m">
<div class="panel-heading b-b small"> <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>
<div class="widget-content no-bg auto wrapper"> <div class="widget-content no-bg auto wrapper">
<table> <table>
@ -13,18 +13,18 @@
<tr><th style="width:60%"></th></tr> <tr><th style="width:60%"></th></tr>
</thead> </thead>
<tbody> <tbody>
<tr><td translate>{{'pricing.code'}}</td> <td>{{coupon.code}}</td></tr> <tr><td translate>{{'app.admin.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>{{'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>{{'pricing.validity_per_user'}}</td> <td translate>{{'pricing.'+coupon.validity_per_user}}</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>{{'pricing.valid_until'}}</td> <td>{{coupon.valid_until | amDateFormat:'L'}}</td></tr> <tr><td translate>{{'app.admin.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>{{'app.admin.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.enabled'}}</td> <td>{{coupon.active | booleanFormat}}</td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="ok()" ng-disabled="ctrl.member == null" translate>{{ 'confirm' }}</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>{{ 'cancel' }}</button> <button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div> </div>

View File

@ -1,10 +1,10 @@
<div class="alert alert-warning m-t"> <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> </div>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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"> <th style="width:20%" ng-repeat="group in enabledGroups">
<span class="text-u-c text-sm">{{group.name}}</span> <span class="text-u-c text-sm">{{group.name}}</span>
</th> </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"> <div ng-show="fablabWithoutPlans" class="alert alert-warning m-t">
{{ 'pricing.beware_the_subscriptions_are_disabled_on_this_application' | translate }} {{ 'app.admin.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 }} {{ 'app.admin.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 }} <br>{{ 'app.admin.pricing.for_safety_reasons_please_dont_create_subscriptions_if_you_dont_want_intend_to_use_them_later' | translate }}
</div> </div>
<div class="m-t-lg"> <div class="m-t-lg">
<button type="button" class="btn btn-warning" ui-sref="app.admin.plans.new"> <button type="button" class="btn btn-warning" ui-sref="app.admin.plans.new">
<i class="fa fa-plus m-r"></i> <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> </button>
<div class="form-group pull-right"> <div class="form-group pull-right">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span> <span class="input-group-addon"><i class="fa fa-filter"></i></span>
<select ng-model="planFiltering" class="form-control"> <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> </select>
</div> </div>
</div> </div>
@ -24,12 +24,12 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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('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')">{{ '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('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')">{{ '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('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')">{{ '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><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('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 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')">{{ '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('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> <th></th>
</tr> </tr>
</thead> </thead>

View File

@ -1,7 +1,7 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <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"> <th style="width:20%" ng-repeat="group in enabledGroups">
<span class="text-u-c text-sm">{{group.name}}</span> <span class="text-u-c text-sm">{{group.name}}</span>
</th> </th>

View File

@ -7,12 +7,12 @@
</div> </div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l"> <div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title"> <section class="heading-title">
<h1 translate>{{ 'project_elements.projects_elements_management' }}</h1> <h1 translate>{{ 'app.admin.project_elements.projects_elements_management' }}</h1>
</section> </section>
</div> </div>
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md"> <div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
<section class="heading-actions wrapper"> <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> </section>
</div> </div>
</div> </div>
@ -24,14 +24,14 @@
<div class="col-md-12"> <div class="col-md-12">
<uib-tabset justified="true"> <uib-tabset justified="true">
<uib-tab heading="{{ 'materials' | translate }}"> <uib-tab heading="{{ 'app.admin.project_elements.materials' | translate }}">
<ng-include src="'<%= asset_path 'admin/project_elements/materials.html' %>'"></ng-include> <ng-include src="'<%= asset_path "admin/project_elements/materials.html" %>'"></ng-include>
</uib-tab> </uib-tab>
<uib-tab heading="{{ 'themes' | translate }}"> <uib-tab heading="{{ 'app.admin.project_elements.themes' | translate }}">
<ng-include src="'<%= asset_path 'admin/project_elements/themes.html' %>'"></ng-include> <ng-include src="'<%= asset_path "admin/project_elements/themes.html" %>'"></ng-include>
</uib-tab> </uib-tab>
<uib-tab heading="{{ 'project_elements.licences' | translate }}"> <uib-tab heading="{{ 'app.admin.project_elements.licences' | translate }}">
<ng-include src="'<%= asset_path 'admin/project_elements/licences.html' %>'"></ng-include> <ng-include src="'<%= asset_path "admin/project_elements/licences.html" %>'"></ng-include>
</uib-tab> </uib-tab>
</uib-tabset> </uib-tabset>
</div> </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"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:30%" translate>{{ 'name' }}</th> <th style="width:30%" translate>{{ 'app.admin.project_elements.name' }}</th>
<th style="width:50%" class="hidden-xs" translate>{{ 'description' }}</th> <th style="width:50%" class="hidden-xs" translate>{{ 'app.admin.project_elements.description' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -32,7 +32,7 @@
</form> </form>
<div class="buttons" ng-show="!rowform.$visible"> <div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
<button class="btn btn-danger" ng-click="removeLicence($index)"> <button class="btn btn-danger" ng-click="removeLicence($index)">
<i class="fa fa-trash-o"></i> <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"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:80%" translate>{{ 'name' }}</th> <th style="width:80%" translate>{{ 'app.admin.project_elements.name' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -26,7 +26,7 @@
</form> </form>
<div class="buttons" ng-show="!rowform.$visible"> <div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
<button class="btn btn-danger" ng-click="removeComponent($index)"> <button class="btn btn-danger" ng-click="removeComponent($index)">
<i class="fa fa-trash-o"></i> <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"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:80%" translate>{{ 'name' }}</th> <th style="width:80%" translate>{{ 'app.admin.project_elements.name' }}</th>
<th style="width:20%"></th> <th style="width:20%"></th>
</tr> </tr>
</thead> </thead>
@ -26,7 +26,7 @@
</form> </form>
<div class="buttons" ng-show="!rowform.$visible"> <div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-default" ng-click="rowform.$show()"> <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>
<button class="btn btn-danger" ng-click="removeTheme($index)"> <button class="btn btn-danger" ng-click="removeTheme($index)">
<i class="fa fa-trash-o"></i> <i class="fa fa-trash-o"></i>

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