init depot fabmanager
29
.gitignore
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||
#
|
||||
# If you find yourself ignoring temporary files generated by your text editor
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile '~/.gitignore_global'
|
||||
|
||||
# Ignore bundler config.
|
||||
/.bundle
|
||||
/vendor/cache
|
||||
|
||||
# Ignore the default SQLite database.
|
||||
/db/*.sqlite3
|
||||
/db/*.sqlite3-journal
|
||||
|
||||
# Ignore all logfiles and tempfiles.
|
||||
/log/*
|
||||
!/log/.keep
|
||||
/tmp
|
||||
|
||||
# uploads and public assets
|
||||
/public/uploads
|
||||
/public/assets
|
||||
|
||||
# MacOS and IDE files
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
# machine specific database config
|
||||
/config/database.yml
|
4
.rspec
Normal file
@ -0,0 +1,4 @@
|
||||
--color
|
||||
--require spec_helper
|
||||
--format documentation
|
||||
--backtrace
|
1
.ruby-gemset
Normal file
@ -0,0 +1 @@
|
||||
fabmanager
|
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
||||
ruby-2.2.1
|
6
Capfile
Normal file
@ -0,0 +1,6 @@
|
||||
load 'deploy'
|
||||
# Uncomment if you are using Rails' asset pipeline
|
||||
load 'deploy/assets'
|
||||
load 'config/deploy' # remove this line to skip loading any of the default tasks
|
||||
|
||||
require 'capistrano/sidekiq'
|
113
Gemfile
Normal file
@ -0,0 +1,113 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||
gem 'rails', '4.2.1'
|
||||
# Use SCSS for stylesheets
|
||||
gem 'sass-rails', '5.0.1'
|
||||
gem 'compass-rails', '2.0.4'
|
||||
|
||||
# Use Uglifier as compressor for JavaScript assets
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
# Use CoffeeScript for .js.coffee assets and views
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
gem 'therubyracer', platforms: :ruby
|
||||
|
||||
# Use jquery as the JavaScript library
|
||||
gem 'jquery-rails'
|
||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||
gem 'jbuilder', '~> 2.0'
|
||||
# bundle exec rake doc:rails generates the API under doc/api.
|
||||
gem 'sdoc', '~> 0.4.0', group: :doc
|
||||
|
||||
gem 'forgery'
|
||||
gem 'responders', '~> 2.0'
|
||||
|
||||
group :development, :test do
|
||||
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||
gem 'byebug'
|
||||
|
||||
# Access an IRB console on exception pages or by using <%= console %> in views
|
||||
gem 'web-console', '~> 2.0'
|
||||
|
||||
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||
gem 'spring'
|
||||
|
||||
gem 'factory_girl_rails'
|
||||
gem 'rspec-rails'
|
||||
gem 'spring-commands-rspec'
|
||||
|
||||
gem 'guard-rspec', require: false
|
||||
end
|
||||
|
||||
group :development do
|
||||
# Preview mail in the browser
|
||||
gem 'letter_opener'
|
||||
gem 'awesome_print'
|
||||
|
||||
gem "puma"
|
||||
gem 'foreman'
|
||||
|
||||
gem 'capistrano'
|
||||
gem 'rvm-capistrano', require: false
|
||||
gem 'capistrano-sidekiq', require: false
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'database_cleaner'
|
||||
gem 'faker'
|
||||
end
|
||||
|
||||
group :production do
|
||||
gem 'unicorn'
|
||||
gem 'rails_12factor'
|
||||
end
|
||||
|
||||
gem 'seed_dump'
|
||||
|
||||
gem 'pg'
|
||||
|
||||
gem 'devise'
|
||||
gem 'devise-async'
|
||||
|
||||
gem 'rolify'
|
||||
|
||||
gem 'kaminari'
|
||||
|
||||
gem 'figaro'
|
||||
|
||||
gem 'bootstrap-sass'
|
||||
gem 'font-awesome-rails'
|
||||
|
||||
gem 'angularjs-rails'
|
||||
|
||||
# Image processing ruby wrapper for ImageMagick
|
||||
gem 'mini_magick'
|
||||
# upload files
|
||||
gem 'carrierwave'
|
||||
|
||||
gem 'twitter'
|
||||
gem 'twitter-text'
|
||||
|
||||
# slug url
|
||||
gem 'friendly_id', '~> 5.1.0'
|
||||
|
||||
# state machine
|
||||
gem 'aasm'
|
||||
|
||||
# Background job processing
|
||||
gem 'sidekiq'
|
||||
gem 'sinatra', require: false
|
||||
# Recurring jobs for Sidekiq
|
||||
gem 'sidekiq-cron'
|
||||
|
||||
gem 'recurrence'
|
||||
|
||||
# Fork de la gem avec support Attachments
|
||||
gem 'mandrill_dm', github: 'AbleTech/mandrill_dm'
|
||||
|
||||
gem 'disqus_api'
|
||||
|
||||
gem 'notify_with'
|
||||
|
||||
gem 'pundit'
|
452
Gemfile.lock
Normal file
@ -0,0 +1,452 @@
|
||||
GIT
|
||||
remote: git://github.com/AbleTech/mandrill_dm.git
|
||||
revision: 2bbb35dd81887bb915f606699d63a723b450711d
|
||||
specs:
|
||||
mandrill_dm (1.1.0)
|
||||
mandrill-api (~> 1.0.51)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
aasm (4.1.0)
|
||||
actionmailer (4.2.1)
|
||||
actionpack (= 4.2.1)
|
||||
actionview (= 4.2.1)
|
||||
activejob (= 4.2.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (4.2.1)
|
||||
actionview (= 4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
||||
actionview (4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
||||
activejob (4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.1)
|
||||
activemodel (= 4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.1)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.8)
|
||||
angularjs-rails (1.3.15)
|
||||
arel (6.0.0)
|
||||
autoprefixer-rails (5.1.8)
|
||||
execjs
|
||||
json
|
||||
awesome_print (1.6.1)
|
||||
bcrypt (3.1.10)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootstrap-sass (3.3.4.1)
|
||||
autoprefixer-rails (>= 5.0.0.1)
|
||||
sass (>= 3.2.19)
|
||||
buftok (0.2.0)
|
||||
builder (3.2.2)
|
||||
byebug (4.0.4)
|
||||
columnize (= 0.9.0)
|
||||
capistrano (2.15.5)
|
||||
highline
|
||||
net-scp (>= 1.0.0)
|
||||
net-sftp (>= 2.0.0)
|
||||
net-ssh (>= 2.0.14)
|
||||
net-ssh-gateway (>= 1.1.0)
|
||||
capistrano-sidekiq (0.5.2)
|
||||
capistrano
|
||||
sidekiq
|
||||
carrierwave (0.10.0)
|
||||
activemodel (>= 3.2.0)
|
||||
activesupport (>= 3.2.0)
|
||||
json (>= 1.7)
|
||||
mime-types (>= 1.16)
|
||||
celluloid (0.16.0)
|
||||
timers (~> 4.0.0)
|
||||
chunky_png (1.3.4)
|
||||
coderay (1.1.0)
|
||||
coffee-rails (4.1.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
coffee-script (2.3.0)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.9.1)
|
||||
columnize (0.9.0)
|
||||
compass (1.0.3)
|
||||
chunky_png (~> 1.2)
|
||||
compass-core (~> 1.0.2)
|
||||
compass-import-once (~> 1.0.5)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
sass (>= 3.3.13, < 3.5)
|
||||
compass-core (1.0.3)
|
||||
multi_json (~> 1.0)
|
||||
sass (>= 3.3.0, < 3.5)
|
||||
compass-import-once (1.0.5)
|
||||
sass (>= 3.2, < 3.5)
|
||||
compass-rails (2.0.4)
|
||||
compass (~> 1.0.0)
|
||||
sass-rails (<= 5.0.1)
|
||||
sprockets (< 2.13)
|
||||
connection_pool (2.1.3)
|
||||
database_cleaner (1.4.1)
|
||||
debug_inspector (0.0.2)
|
||||
devise (3.4.1)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
responders
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
devise-async (0.9.0)
|
||||
devise (~> 3.2)
|
||||
diff-lcs (1.2.5)
|
||||
disqus_api (0.0.4)
|
||||
activesupport (>= 3.0.0)
|
||||
faraday (>= 0.8)
|
||||
faraday_middleware (>= 0.9)
|
||||
equalizer (0.0.11)
|
||||
erubis (2.7.0)
|
||||
excon (0.45.1)
|
||||
execjs (2.4.0)
|
||||
factory_girl (4.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.5.0)
|
||||
factory_girl (~> 4.5.0)
|
||||
railties (>= 3.0.0)
|
||||
faker (1.4.3)
|
||||
i18n (~> 0.5)
|
||||
faraday (0.9.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday_middleware (0.9.1)
|
||||
faraday (>= 0.7.4, < 0.10)
|
||||
ffi (1.9.8)
|
||||
figaro (1.1.0)
|
||||
thor (~> 0.14)
|
||||
font-awesome-rails (4.3.0.0)
|
||||
railties (>= 3.2, < 5.0)
|
||||
foreman (0.78.0)
|
||||
thor (~> 0.19.1)
|
||||
forgery (0.6.0)
|
||||
formatador (0.2.5)
|
||||
friendly_id (5.1.0)
|
||||
activerecord (>= 4.0.0)
|
||||
globalid (0.3.3)
|
||||
activesupport (>= 4.1.0)
|
||||
guard (2.12.5)
|
||||
formatador (>= 0.2.4)
|
||||
listen (~> 2.7)
|
||||
lumberjack (~> 1.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-compat (1.2.1)
|
||||
guard-rspec (4.5.0)
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
highline (1.7.1)
|
||||
hike (1.2.3)
|
||||
hitimes (1.2.2)
|
||||
http (0.6.4)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
http_parser.rb (0.6.0)
|
||||
i18n (0.7.0)
|
||||
jbuilder (2.2.12)
|
||||
activesupport (>= 3.0.0, < 5)
|
||||
multi_json (~> 1.2)
|
||||
jquery-rails (4.0.3)
|
||||
rails-dom-testing (~> 1.0)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (1.8.2)
|
||||
kaminari (0.16.3)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.9.3)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.3.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.7)
|
||||
listen (2.10.0)
|
||||
celluloid (~> 0.16.0)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
loofah (2.0.1)
|
||||
nokogiri (>= 1.5.9)
|
||||
lumberjack (1.0.9)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
mandrill-api (1.0.53)
|
||||
excon (>= 0.16.0, < 1.0)
|
||||
json (>= 1.7.7, < 2.0)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.4.3)
|
||||
mini_magick (4.2.0)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.5.1)
|
||||
multi_json (1.11.0)
|
||||
multipart-post (2.0.0)
|
||||
naught (1.0.0)
|
||||
nenv (0.2.0)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-sftp (2.1.2)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.9.2)
|
||||
net-ssh-gateway (1.2.0)
|
||||
net-ssh (>= 2.6.5)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
notiffany (0.0.6)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
notify_with (0.0.2)
|
||||
jbuilder (~> 2.0)
|
||||
rails (>= 4.2.0)
|
||||
responders (~> 2.0)
|
||||
orm_adapter (0.5.0)
|
||||
pg (0.18.1)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
puma (2.11.1)
|
||||
rack (>= 1.1, < 2.0)
|
||||
pundit (1.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
rack (1.6.0)
|
||||
rack-protection (1.5.3)
|
||||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.1)
|
||||
actionmailer (= 4.2.1)
|
||||
actionpack (= 4.2.1)
|
||||
actionview (= 4.2.1)
|
||||
activejob (= 4.2.1)
|
||||
activemodel (= 4.2.1)
|
||||
activerecord (= 4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.1)
|
||||
sprockets-rails
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.6)
|
||||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.2)
|
||||
loofah (~> 2.0)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.4)
|
||||
rails_stdout_logging (0.0.3)
|
||||
railties (4.2.1)
|
||||
actionpack (= 4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
raindrops (0.13.0)
|
||||
rake (10.4.2)
|
||||
rb-fsevent (0.9.4)
|
||||
rb-inotify (0.9.5)
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (4.2.0)
|
||||
recurrence (1.3.0)
|
||||
activesupport
|
||||
i18n
|
||||
redis (3.2.1)
|
||||
redis-namespace (1.5.2)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
ref (1.0.5)
|
||||
responders (2.1.0)
|
||||
railties (>= 4.2.0, < 5)
|
||||
rolify (4.0.0)
|
||||
rspec (3.2.0)
|
||||
rspec-core (~> 3.2.0)
|
||||
rspec-expectations (~> 3.2.0)
|
||||
rspec-mocks (~> 3.2.0)
|
||||
rspec-core (3.2.2)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-expectations (3.2.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-mocks (3.2.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-rails (3.2.1)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.2.0)
|
||||
rspec-expectations (~> 3.2.0)
|
||||
rspec-mocks (~> 3.2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-support (3.2.2)
|
||||
rufus-scheduler (3.0.9)
|
||||
tzinfo
|
||||
rvm-capistrano (1.5.6)
|
||||
capistrano (~> 2.15.4)
|
||||
sass (3.4.13)
|
||||
sass-rails (5.0.1)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (~> 1.1)
|
||||
sdoc (0.4.1)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
rdoc (~> 4.0)
|
||||
seed_dump (3.2.2)
|
||||
activerecord (~> 4)
|
||||
activesupport (~> 4)
|
||||
shellany (0.0.1)
|
||||
sidekiq (3.3.3)
|
||||
celluloid (>= 0.16.0)
|
||||
connection_pool (>= 2.1.1)
|
||||
json
|
||||
redis (>= 3.0.6)
|
||||
redis-namespace (>= 1.3.1)
|
||||
sidekiq-cron (0.2.0)
|
||||
rufus-scheduler (>= 2.0.24)
|
||||
sidekiq (>= 2.17.3)
|
||||
tilt (< 2.0.0)
|
||||
simple_oauth (0.3.1)
|
||||
sinatra (1.4.6)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (>= 1.3, < 3)
|
||||
slop (3.6.0)
|
||||
spring (1.3.3)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
sprockets (2.12.3)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-rails (2.2.4)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
therubyracer (0.12.1)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
tilt (1.4.1)
|
||||
timers (4.0.1)
|
||||
hitimes
|
||||
twitter (5.14.0)
|
||||
addressable (~> 2.3)
|
||||
buftok (~> 0.2.0)
|
||||
equalizer (~> 0.0.9)
|
||||
faraday (~> 0.9.0)
|
||||
http (~> 0.6.0)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
json (~> 1.8)
|
||||
memoizable (~> 0.4.0)
|
||||
naught (~> 1.0)
|
||||
simple_oauth (~> 0.3.0)
|
||||
twitter-text (1.11.0)
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.7.1)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.6)
|
||||
unicorn (4.8.3)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
warden (1.2.3)
|
||||
rack (>= 1.0)
|
||||
web-console (2.1.2)
|
||||
activemodel (>= 4.0)
|
||||
binding_of_caller (>= 0.7.2)
|
||||
railties (>= 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
aasm
|
||||
angularjs-rails
|
||||
awesome_print
|
||||
bootstrap-sass
|
||||
byebug
|
||||
capistrano
|
||||
capistrano-sidekiq
|
||||
carrierwave
|
||||
coffee-rails (~> 4.1.0)
|
||||
compass-rails (= 2.0.4)
|
||||
database_cleaner
|
||||
devise
|
||||
devise-async
|
||||
disqus_api
|
||||
factory_girl_rails
|
||||
faker
|
||||
figaro
|
||||
font-awesome-rails
|
||||
foreman
|
||||
forgery
|
||||
friendly_id (~> 5.1.0)
|
||||
guard-rspec
|
||||
jbuilder (~> 2.0)
|
||||
jquery-rails
|
||||
kaminari
|
||||
letter_opener
|
||||
mandrill_dm!
|
||||
mini_magick
|
||||
notify_with
|
||||
pg
|
||||
puma
|
||||
pundit
|
||||
rails (= 4.2.1)
|
||||
rails_12factor
|
||||
recurrence
|
||||
responders (~> 2.0)
|
||||
rolify
|
||||
rspec-rails
|
||||
rvm-capistrano
|
||||
sass-rails (= 5.0.1)
|
||||
sdoc (~> 0.4.0)
|
||||
seed_dump
|
||||
sidekiq
|
||||
sidekiq-cron
|
||||
sinatra
|
||||
spring
|
||||
spring-commands-rspec
|
||||
therubyracer
|
||||
twitter
|
||||
twitter-text
|
||||
uglifier (>= 1.3.0)
|
||||
unicorn
|
||||
web-console (~> 2.0)
|
86
Guardfile
Normal file
@ -0,0 +1,86 @@
|
||||
# A sample Guardfile
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
## Uncomment and set this to only include directories you want to watch
|
||||
# directories %w(app lib config test spec features)
|
||||
|
||||
## Uncomment to clear the screen before every task
|
||||
# clearing :on
|
||||
|
||||
## Guard internally checks for changes in the Guardfile and exits.
|
||||
## If you want Guard to automatically start up again, run guard in a
|
||||
## shell loop, e.g.:
|
||||
##
|
||||
## $ while bundle exec guard; do echo "Restarting Guard..."; done
|
||||
##
|
||||
## Note: if you are using the `directories` clause above and you are not
|
||||
## watching the project directory ('.'), then you will want to move
|
||||
## the Guardfile to a watched dir and symlink it back, e.g.
|
||||
#
|
||||
# $ mkdir config
|
||||
# $ mv Guardfile config/
|
||||
# $ ln -s config/Guardfile .
|
||||
#
|
||||
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
||||
|
||||
# Note: The cmd option is now required due to the increasing number of ways
|
||||
# rspec may be run, below are examples of the most common uses.
|
||||
# * bundler: 'bundle exec rspec'
|
||||
# * bundler binstubs: 'bin/rspec'
|
||||
# * spring: 'bin/rspec' (This will use spring if running and you have
|
||||
# installed the spring binstubs per the docs)
|
||||
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
||||
# * 'just' rspec: 'rspec'
|
||||
|
||||
guard :rspec, cmd: "bundle exec spring rspec" do
|
||||
require "guard/rspec/dsl"
|
||||
dsl = Guard::RSpec::Dsl.new(self)
|
||||
|
||||
# Feel free to open issues for suggestions and improvements
|
||||
|
||||
# RSpec files
|
||||
rspec = dsl.rspec
|
||||
watch(rspec.spec_helper) { rspec.spec_dir }
|
||||
watch(rspec.spec_support) { rspec.spec_dir }
|
||||
watch(rspec.spec_files)
|
||||
|
||||
# Ruby files
|
||||
ruby = dsl.ruby
|
||||
dsl.watch_spec_files_for(ruby.lib_files)
|
||||
|
||||
# Rails files
|
||||
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
||||
dsl.watch_spec_files_for(rails.app_files)
|
||||
dsl.watch_spec_files_for(rails.views)
|
||||
|
||||
watch(rails.controllers) do |m|
|
||||
[
|
||||
rspec.spec.("routing/#{m[1]}_routing"),
|
||||
rspec.spec.("controllers/#{m[1]}_controller"),
|
||||
rspec.spec.("acceptance/#{m[1]}")
|
||||
]
|
||||
end
|
||||
|
||||
# Rails config changes
|
||||
watch(rails.spec_helper) { rspec.spec_dir }
|
||||
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
||||
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
||||
|
||||
# Capybara features specs
|
||||
watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
|
||||
|
||||
# Turnip features and steps
|
||||
watch(%r{^spec/acceptance/(.+)\.feature$})
|
||||
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
||||
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
||||
end
|
||||
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
watch('config/routes.rb') { "spec/routing" }
|
||||
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
||||
end
|
2
Procfile
Normal file
@ -0,0 +1,2 @@
|
||||
web: bundle exec rails server puma -p $PORT
|
||||
worker: bundle exec sidekiq -C ./config/sidekiq.yml
|
127
README.md
Normal file
@ -0,0 +1,127 @@
|
||||
# README
|
||||
|
||||
This project is the FabLab Manager web application.
|
||||
|
||||
The purpose of this web application is to allow users to document their FabLab projects. The FabLab also have the ability
|
||||
to plan some events (workshops or courses) and to expose them to its users.
|
||||
|
||||
This product can be extended to be used as a complete internal management system for a FabLab.
|
||||
|
||||
The underlying technologies are:
|
||||
- `Ruby on Rails` for the backend application (server RESTful API)
|
||||
- `AngularJS` for the frontend application (web-based graphical user interface)
|
||||
|
||||
|
||||
|
||||
## 1. Configuration
|
||||
|
||||
The following files must be filled with the correct configuration to allow FabManager to run correctly:
|
||||
|
||||
- config/environments/production.rb
|
||||
- `mandrill` -> change this if you're using a different mailing system
|
||||
|
||||
- config/environments/staging.rb
|
||||
- `config.action_mailer.default_url_options` -> change the URL according to the staging deployment url
|
||||
- `mandrill` -> change this if you're using a different mailing system
|
||||
|
||||
- config/application.yml
|
||||
- `DEVISE_KEY` -> generate any secret phrase to secure the Devise authentication. You can use the `$ rake secret` command for this purpose.
|
||||
- `SECRET_KEY_BASE` -> generate any secret phrase here to prevent XSS attacks. You can use the `$ rake secret` command for this purpose.
|
||||
- `DEFAULT_MAIL_FROM` -> default e-mail address from which the emails are sent
|
||||
- `MANDRILL_USERNAME` -> if you plan to use mandrill
|
||||
- `MANDRILL_APIKEY` -> if you plan to use mandrill
|
||||
- `TWITTER_NAME` -> twitter api configuration
|
||||
- `TWITTER_CONSUMER_KEY` -> twitter api configuration
|
||||
- `TWITTER_CONSUMER_SECRET` -> twitter api configuration
|
||||
- `TWITTER_ACCESS_TOKEN` -> twitter api configuration
|
||||
- `TWITTER_ACCESS_TOKEN_SECRET` -> twitter api configuration
|
||||
- `GOOGLE_ANALYTICS_ACCOUNT` -> Google analytics account identifier (if you want to use GA)
|
||||
- `APPLICATION_ROOT_URL` -> The public URL where you application is deployed in production (eg. fablab.lacasemate.com)
|
||||
|
||||
- config/mandrill.rb
|
||||
You may change this if you don't want to use mandrill as your production mailing system
|
||||
|
||||
- config/database.yml.default
|
||||
Copy/Paste this file to `config/database.yml` and modify the configuration according to your postgreSQL configuration
|
||||
|
||||
- config/disqus_api.yml
|
||||
Insert here your identifiers for the Disqus API
|
||||
|
||||
|
||||
|
||||
## 2. Setup a development environment
|
||||
|
||||
1. Install RVM with latest ruby version
|
||||
See http://rvm.io/rvm/install
|
||||
|
||||
2. Retrieve the project from Git
|
||||
`$ git clone git@github.com:LaCasemate/fab-manager.git`
|
||||
|
||||
3. Install the dependencies
|
||||
- Ubuntu: `$ sudo apt-get install libpq-dev postgresql redis-server imagemagick`
|
||||
- MacOS: `$ brew install postgresql redis imagemagick`
|
||||
|
||||
4. Init the RVM instance and check it was correctly configured
|
||||
```
|
||||
$ cd fab-manager
|
||||
$ rvm current
|
||||
```
|
||||
|
||||
5. Setup the project requirements
|
||||
`$ bundle install`
|
||||
|
||||
6. Build the database. You may have to configure your postgreSQL instance before, as described in chapter `3.2 Setup the FabManager database in PostgreSQL`
|
||||
`$ rake db:setup`
|
||||
|
||||
7. Create the pids folder used by sidekiq. If you want to use a different location, you can configure it in `config/sidekiq.yml`
|
||||
`$ mkdir -p tmp/pids`
|
||||
|
||||
8. Configure the application environment variables, as explained in chapter `1. Configuration`
|
||||
|
||||
9. Start the development web server
|
||||
`$ foreman s -p 3000`
|
||||
|
||||
|
||||
|
||||
## 3. PostgreSQL
|
||||
|
||||
### 3.1 Launch PostgreSQL on MacOS
|
||||
|
||||
$ ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents
|
||||
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
|
||||
|
||||
The first command will start postgresql at login with launchd. The second will load postgresql now.
|
||||
|
||||
### 3.2 Setup the FabManager database in PostgreSQL
|
||||
|
||||
1. Login as the postgres user
|
||||
`$ sudo -i -u postgres`
|
||||
|
||||
2. Run the postgreSQL administration command line interface
|
||||
`$ psql`
|
||||
|
||||
3. Create a new user in postgres (in this example, the user will be named "sleede")
|
||||
`# CREATE USER sleede;`
|
||||
|
||||
4. Grant him the right to create databases
|
||||
`# ALTER ROLE sleede WITH CREATEDB;`
|
||||
|
||||
5. Then create the fablab database
|
||||
`# CREATE DATABASE fabmanager_development OWNER sleede;`
|
||||
|
||||
6. To finish, attribute a password to this user
|
||||
`# ALTER USER sleede WITH ENCRYPTED PASSWORD 'sleede';`
|
||||
|
||||
|
||||
|
||||
## 4. Know issues
|
||||
|
||||
If you encounter a problem with bundler (unable to run `$ rails c` or `$ rails g`), you can fix it running the following commands:
|
||||
|
||||
$ bundle pack
|
||||
$ bundle install --path vendor/cache
|
||||
|
||||
|
||||
|
||||
## 5. Related Documentation
|
||||
- Angular-Bootstrap: http://angular-ui.github.io/bootstrap/
|
6
Rakefile
Normal file
@ -0,0 +1,6 @@
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
|
||||
Rails.application.load_tasks
|
0
app/assets/images/.keep
Normal file
BIN
app/assets/images/about-fablab.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
app/assets/images/fablab-w.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
app/assets/images/fablab.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
app/assets/images/fablab.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/assets/images/favicons/favicon.ico
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/assets/images/fleche-left.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
app/assets/images/no_avatar.png
Executable file
After Width: | Height: | Size: 619 B |
BIN
app/assets/images/select2/select2-spinner.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
app/assets/images/select2/select2.png
Normal file
After Width: | Height: | Size: 613 B |
BIN
app/assets/images/select2/select2x2.png
Normal file
After Width: | Height: | Size: 845 B |
91
app/assets/javascripts/app.js.erb
Normal file
@ -0,0 +1,91 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The application file bootstraps the angular app by initializing the main module and
|
||||
* creating namespaces and moduled for controllers, filters, services, and directives.
|
||||
*/
|
||||
|
||||
var Application = Application || {};
|
||||
|
||||
Application.Constants = angular.module('application.constants', []);
|
||||
Application.Services = angular.module('application.services', []);
|
||||
Application.Controllers = angular.module('application.controllers', []);
|
||||
Application.Filters = angular.module('application.filters', []);
|
||||
Application.Directives = angular.module('application.directives', []);
|
||||
|
||||
|
||||
angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ngAnimate', 'ngCookies', 'ui.router', 'ui.bootstrap',
|
||||
'ngUpload', 'duScroll', 'application.filters','application.services', 'application.directives',
|
||||
'application.constants', 'application.controllers', 'application.router', 'ui.select2', 'angularMoment',
|
||||
'Devise', 'DeviseModal', 'angular-growl', 'xeditable', 'checklist-model', 'unsavedChanges', 'angular-loading-bar',
|
||||
'ngTouch', 'angular-google-analytics', 'angularUtils.directives.dirDisqus', 'angular-redactor']).
|
||||
config(['$locationProvider', '$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfigProvider", "AnalyticsProvider", "datepickerPopupConfig",
|
||||
function($locationProvider, $httpProvider, AuthProvider, growlProvider, unsavedWarningsConfigProvider, AnalyticsProvider, datepickerPopupConfig) {
|
||||
|
||||
|
||||
<% if Rails.env.production? and ENV["GOOGLE_ANALYTICS_ACCOUNT"] != 'UA-YOUR_ID_HERE' and ENV["GOOGLE_ANALYTICS_ACCOUNT"] != nil %>
|
||||
AnalyticsProvider.setAccount('<%= ENV["GOOGLE_ANALYTICS_ACCOUNT"] %>');
|
||||
// track all routes (or not)
|
||||
AnalyticsProvider.trackPages(true);
|
||||
AnalyticsProvider.setDomainName('<%= ENV["APPLICATION_ROOT_URL"] %>');
|
||||
AnalyticsProvider.useAnalytics(true);
|
||||
AnalyticsProvider.setPageEvent('$stateChangeSuccess');
|
||||
<% else %>
|
||||
AnalyticsProvider.setAccount('DISABLED');
|
||||
<% end %>
|
||||
|
||||
datepickerPopupConfig.closeText = "Fermer";
|
||||
datepickerPopupConfig.cleartext = "Effacer";
|
||||
datepickerPopupConfig.currentText = "Aujourd'hui";
|
||||
|
||||
// custom message for angular-unsavedChanges
|
||||
unsavedWarningsConfigProvider.navigateMessage = "Vous perdrez les modifications non enregistrées si vous quittez cette page";
|
||||
unsavedWarningsConfigProvider.reloadMessage = "Vous perdrez les modifications non enregistrées si vous rechargez cette page";
|
||||
|
||||
growlProvider.globalTimeToLive(5000);
|
||||
growlProvider.globalEnableHtml(true);
|
||||
|
||||
$locationProvider.hashPrefix('!');
|
||||
|
||||
}]).run(["$rootScope", "$log", "AuthService", "Auth", "amMoment", "$state", "editableOptions", "$location", "Analytics", function($rootScope, $log, AuthService, Auth, amMoment, $state, editableOptions, $location, Analytics){
|
||||
|
||||
amMoment.changeLocale('fr');
|
||||
|
||||
editableOptions.theme = 'bs3';
|
||||
|
||||
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){
|
||||
$state.prevState = fromState;
|
||||
$state.prevParams = fromParams;
|
||||
});
|
||||
|
||||
$rootScope.backPrevLocation = function(event){
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if($state.prevState.name == ""){
|
||||
$state.prevState = "app.public.home";
|
||||
}
|
||||
$state.go($state.prevState, $state.prevParams);
|
||||
};
|
||||
|
||||
}]).filter('array', function() {
|
||||
return function(arrayLength) {
|
||||
if (arrayLength) {
|
||||
arrayLength = Math.ceil(arrayLength);
|
||||
var arr = new Array(arrayLength), i = 0;
|
||||
for (; i < arrayLength; i++) {
|
||||
arr[i] = i;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
};
|
||||
}).directive('datepickerPopup', function (){
|
||||
// fixes https://github.com/angular-ui/bootstrap/issues/2659
|
||||
return {
|
||||
restrict: 'EAC',
|
||||
require: 'ngModel',
|
||||
link: function(scope, element, attr, controller) {
|
||||
//remove the default formatter from the input directive to prevent conflict
|
||||
controller.$formatters.shift();
|
||||
}
|
||||
}
|
||||
});
|
59
app/assets/javascripts/application.js
Normal file
@ -0,0 +1,59 @@
|
||||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||
// listed below.
|
||||
//
|
||||
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
||||
//
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// compiled file.
|
||||
//
|
||||
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require jquery-ui/ui/jquery.ui.core
|
||||
//= require jquery-ui/ui/jquery.ui.widget
|
||||
//= require jquery-ui/ui/jquery.ui.mouse
|
||||
//= require jquery-ui/ui/jquery.ui.draggable
|
||||
//= require jquery-ui/ui/jquery.ui.droppable
|
||||
//= require jquery-ui/ui/jquery.ui.resizable
|
||||
//= require angular
|
||||
//= require angular-i18n/angular-locale_fr-fr.js
|
||||
//= require angular-cookies
|
||||
//= require angular-resource
|
||||
//= require angular-sanitize
|
||||
//= require angular-animate
|
||||
//= require angular-cookies
|
||||
//= require angular-touch
|
||||
//= require angular-ui-router/release/angular-ui-router
|
||||
//= require angular-bootstrap/ui-bootstrap-tpls
|
||||
//= require select2/select2
|
||||
//= require select2/select2_locale_fr
|
||||
//= require angular-ui-select2/src/select2
|
||||
//= require moment/moment
|
||||
//= require moment/locale/fr
|
||||
//= require angular-moment/angular-moment
|
||||
//= require ngUpload/ng-upload
|
||||
//= require jasny-bootstrap/js/fileinput
|
||||
//= require holderjs/holder
|
||||
//= require angular-devise/lib/devise
|
||||
//= require devise-modal
|
||||
//= require angular-growl/build/angular-growl
|
||||
//= require angular-xeditable/dist/js/xeditable
|
||||
//= require checklist-model/checklist-model
|
||||
//= require angular-unsavedChanges/src/unsavedChanges
|
||||
//= require angular-loading-bar/src/loading-bar
|
||||
//= require angular-scroll/angular-scroll
|
||||
//= require angular-google-analytics/src/angular-google-analytics
|
||||
//= require dirDisqus
|
||||
//= require humanize
|
||||
//= require redactor
|
||||
//= require angular-redactor/angular-redactor
|
||||
//= require underscore/underscore
|
||||
//= require app
|
||||
//= require router
|
||||
//= require_tree ./controllers
|
||||
//= require_tree ./services
|
||||
//= require_tree ./directives
|
||||
//= require_tree ./filters
|
242
app/assets/javascripts/controllers/admin/events.coffee
Normal file
@ -0,0 +1,242 @@
|
||||
'use strict'
|
||||
|
||||
### COMMON CODE ###
|
||||
|
||||
##
|
||||
# Provides a set of common properties and methods to the $scope parameter. They are used
|
||||
# in the various events' admin controllers.
|
||||
#
|
||||
# Provides :
|
||||
# - $scope.categories = [{Category}]
|
||||
# - $scope.datePicker = {}
|
||||
# - $scope.submited(content)
|
||||
# - $scope.cancel()
|
||||
# - $scope.addFile()
|
||||
# - $scope.deleteFile(file)
|
||||
# - $scope.fileinputClass(v)
|
||||
# - $scope.openStartDatePicker($event)
|
||||
# - $scope.openEndDatePicker($event)
|
||||
# - $scope.toggleRecurrenceEnd(e)
|
||||
#
|
||||
# Requires :
|
||||
# - $scope.event.event_files_attributes = []
|
||||
# - $state (Ui-Router) [ 'app.public.events_list' ]
|
||||
##
|
||||
class EventsController
|
||||
constructor: ($scope, $state, Event, Category) ->
|
||||
|
||||
## Retrieve the list of categories from the server (stage, atelier, ...)
|
||||
Category.query().$promise.then (data)->
|
||||
$scope.categories = data.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
## default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker =
|
||||
format: 'dd/MM/yyyy'
|
||||
startOpened: false # default: datePicker is not shown
|
||||
endOpened: false
|
||||
recurrenceEndOpened: false
|
||||
options:
|
||||
startingDay: 1 # France: the week starts on monday
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
# Intended to be the callback when an upload is done: any raised error will be stacked in the
|
||||
# $scope.alerts array. If everything goes fine, the user is redirected to the project page.
|
||||
# @param content {Object} JSON - The upload's result
|
||||
##
|
||||
$scope.submited = (content) ->
|
||||
if !content.id?
|
||||
$scope.alerts = []
|
||||
angular.forEach content, (v, k)->
|
||||
angular.forEach v, (err)->
|
||||
$scope.alerts.push({msg: k+': '+err, type: 'danger'})
|
||||
else
|
||||
$state.go('app.public.events_list')
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Changes the user's view to the events list page
|
||||
##
|
||||
$scope.cancel = ->
|
||||
$state.go('app.public.events_list')
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
# The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
# @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
|
||||
##
|
||||
$scope.fileinputClass = (v)->
|
||||
if v
|
||||
'fileinput-exists'
|
||||
else
|
||||
'fileinput-new'
|
||||
|
||||
|
||||
|
||||
##
|
||||
# This will create a single new empty entry into the event's attachements list.
|
||||
##
|
||||
$scope.addFile = ->
|
||||
$scope.event.event_files_attributes.push {}
|
||||
|
||||
|
||||
|
||||
##
|
||||
# This will remove the given file from the event's attachements list. If the file was previously uploaded
|
||||
# to the server, it will be marked for deletion on the server. Otherwise, it will be simply truncated from
|
||||
# the attachements array.
|
||||
# @param file {Object} the file to delete
|
||||
##
|
||||
$scope.deleteFile = (file) ->
|
||||
index = $scope.event.event_files_attributes.indexOf(file)
|
||||
if file.id?
|
||||
file._destroy = true
|
||||
else
|
||||
$scope.event.event_files_attributes.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Show/Hide the "start" datepicker (open the drop down/close it)
|
||||
##
|
||||
$scope.toggleStartDatePicker = ($event) ->
|
||||
$event.preventDefault()
|
||||
$event.stopPropagation()
|
||||
$scope.datePicker.startOpened = !$scope.datePicker.startOpened
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Show/Hide the "end" datepicker (open the drop down/close it)
|
||||
##
|
||||
$scope.toggleEndDatePicker = ($event) ->
|
||||
$event.preventDefault()
|
||||
$event.stopPropagation()
|
||||
$scope.datePicker.endOpened = !$scope.datePicker.endOpened
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Masks/displays the recurrence pane allowing the admin to set the current event as recursive
|
||||
##
|
||||
$scope.toggleRecurrenceEnd = (e)->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
$scope.datePicker.recurrenceEndOpened = !$scope.datePicker.recurrenceEndOpened
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the events listing page (admin view)
|
||||
##
|
||||
Application.Controllers.controller "adminEventsController", ["$scope", "$state", 'Event', ($scope, $state, Event) ->
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## The events displayed on the page
|
||||
$scope.events = []
|
||||
|
||||
## By default, the pagination mode is activated to limit the page size
|
||||
$scope.paginateActive = true
|
||||
|
||||
## The currently displayed page number
|
||||
$scope.page = 1
|
||||
|
||||
|
||||
##
|
||||
# Adds a bucket of events to the bottom of the page, grouped by month
|
||||
##
|
||||
$scope.loadMoreEvents = ->
|
||||
Event.query {page: $scope.page}, (data)->
|
||||
$scope.events = $scope.events.concat data
|
||||
if data.length
|
||||
$scope.paginateActive = false if $scope.events.length >= data[0].nb_total_events
|
||||
else
|
||||
$scope.paginateActive = false
|
||||
$scope.page += 1
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
$scope.loadMoreEvents()
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the event creation page
|
||||
##
|
||||
Application.Controllers.controller "newEventController", ["$scope", "$state", 'Event', 'Category', 'CSRF', ($scope, $state, Event, Category, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/events/"
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'post'
|
||||
|
||||
## Default event parameters
|
||||
$scope.event =
|
||||
event_files_attributes: []
|
||||
start_date: new Date()
|
||||
end_date: new Date()
|
||||
start_time: new Date()
|
||||
end_time: new Date()
|
||||
all_day: 'false'
|
||||
recurrence: 'none'
|
||||
|
||||
## Possible types of recurrences for an event
|
||||
$scope.recurrenceTypes = [
|
||||
{label: 'Aucune', value: 'none'},
|
||||
{label: 'Tous les jours', value: 'day'},
|
||||
{label: 'Chaque semaine', value: 'week'},
|
||||
{label: 'Chaque mois', value: 'month'},
|
||||
{label: 'Chaque année', value: 'year'}
|
||||
]
|
||||
|
||||
## Using the EventsController
|
||||
new EventsController($scope, $state, Event, Category)
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the events edition page
|
||||
##
|
||||
Application.Controllers.controller "editEventController", ["$scope", "$state", "$stateParams", 'Event', 'Category', 'CSRF', ($scope, $state, $stateParams, Event, Category, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/events/" + $stateParams.id
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'put'
|
||||
|
||||
## Retrieve the event details, in case of error the user is redirected to the events listing
|
||||
Event.get {id: $stateParams.id}
|
||||
, (event)->
|
||||
$scope.event = event
|
||||
return
|
||||
, ->
|
||||
$state.go('app.public.events_list')
|
||||
|
||||
## Using the EventsController
|
||||
new EventsController($scope, $state, Event, Category)
|
||||
]
|
153
app/assets/javascripts/controllers/admin/members.coffee
Normal file
@ -0,0 +1,153 @@
|
||||
'use strict'
|
||||
|
||||
### COMMON CODE ###
|
||||
|
||||
##
|
||||
# Provides a set of common properties and methods to the $scope parameter. They are used
|
||||
# in the various members' admin controllers.
|
||||
#
|
||||
# Provides :
|
||||
# - $scope.groups = [{Group}]
|
||||
# - $scope.datePicker = {}
|
||||
# - $scope.submited(content)
|
||||
# - $scope.cancel()
|
||||
# - $scope.fileinputClass(v)
|
||||
# - $scope.openDatePicker($event)
|
||||
#
|
||||
# Requires :
|
||||
# - $state (Ui-Router) [ 'app.admin.members' ]
|
||||
##
|
||||
class MembersController
|
||||
constructor: ($scope, $state, Group) ->
|
||||
|
||||
## Retrieve the profiles groups (eg. students ...)
|
||||
Group.query (groups) ->
|
||||
$scope.groups = groups
|
||||
$scope.user.group_id = $scope.groups[0].id
|
||||
|
||||
## Default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker =
|
||||
format: 'dd/MM/yyyy'
|
||||
opened: false # default: datePicker is not shown
|
||||
options:
|
||||
startingDay: 1 # France: the week starts on monday
|
||||
|
||||
|
||||
##
|
||||
# Shows the birth day datepicker
|
||||
# @param $event {Object} jQuery event object
|
||||
##
|
||||
$scope.openDatePicker = ($event) ->
|
||||
$event.preventDefault()
|
||||
$event.stopPropagation()
|
||||
$scope.datePicker.opened = true
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
# Intended to be the callback when an upload is done: any raised error will be stacked in the
|
||||
# $scope.alerts array. If everything goes fine, the user is redirected to the members listing page.
|
||||
# @param content {Object} JSON - The upload's result
|
||||
##
|
||||
$scope.submited = (content) ->
|
||||
if !content.id?
|
||||
$scope.alerts = []
|
||||
angular.forEach content, (v, k)->
|
||||
angular.forEach v, (err)->
|
||||
$scope.alerts.push
|
||||
msg: k+': '+err,
|
||||
type: 'danger'
|
||||
else
|
||||
$state.go('app.admin.members')
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Changes the admin's view to the members list page
|
||||
##
|
||||
$scope.cancel = ->
|
||||
$state.go('app.admin.members')
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
# The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
# @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
|
||||
##
|
||||
$scope.fileinputClass = (v)->
|
||||
if v
|
||||
'fileinput-exists'
|
||||
else
|
||||
'fileinput-new'
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the member edition page
|
||||
##
|
||||
Application.Controllers.controller "editMemberController", ["$scope", "$state", "$stateParams", "Member", 'dialogs', 'growl', 'Group', 'CSRF', ($scope, $state, $stateParams, Member, dialogs, growl, Group, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/members/" + $stateParams.id
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'patch'
|
||||
|
||||
## The user to edit
|
||||
$scope.user = {}
|
||||
|
||||
## Profiles types (student/standard/...)
|
||||
$scope.groups = []
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
## Retrieve the member's profile details
|
||||
Member.get {id: $stateParams.id}, (resp)->
|
||||
$scope.user = resp
|
||||
|
||||
## Using the MembersController
|
||||
new MembersController($scope, $state, Group)
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the member's creation page (admin view)
|
||||
##
|
||||
Application.Controllers.controller "newMemberController", ["$scope", "$state", "$stateParams", "Member", 'Group', 'CSRF', ($scope, $state, $stateParams, Member, Group, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/members"
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'post'
|
||||
|
||||
## Default member's profile parameters
|
||||
$scope.user =
|
||||
plan_interval: ''
|
||||
|
||||
|
||||
|
||||
## Using the MembersController
|
||||
new MembersController($scope, $state, Group)
|
||||
]
|
157
app/assets/javascripts/controllers/admin/project_elements.coffee
Normal file
@ -0,0 +1,157 @@
|
||||
'use strict'
|
||||
|
||||
Application.Controllers.controller "projectElementsController", ["$scope", "$state", 'Component', 'Licence', 'Theme', ($scope, $state, Component, Licence, Theme) ->
|
||||
|
||||
## Materials list (plastic, wood ...)
|
||||
$scope.components = Component.query()
|
||||
|
||||
## Licences list (Creative Common ...)
|
||||
$scope.licences = Licence.query()
|
||||
|
||||
## Themes list (cooking, sport ...)
|
||||
$scope.themes = Theme.query()
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Saves a new component / Update an existing material to the server (form validation callback)
|
||||
# @param data {Object} component name
|
||||
# @param [data] {number} component id, in case of update
|
||||
##
|
||||
$scope.saveComponent = (data, id) ->
|
||||
if id?
|
||||
Component.update {id: id}, data
|
||||
else
|
||||
Component.save data, (resp)->
|
||||
$scope.components[$scope.components.length-1].id = resp.id
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Deletes the component at the specified index
|
||||
# @param index {number} component index in the $scope.components array
|
||||
##
|
||||
$scope.removeComponent = (index) ->
|
||||
Component.delete $scope.components[index]
|
||||
$scope.components.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Creates a new empty entry in the $scope.components array
|
||||
##
|
||||
$scope.addComponent = ->
|
||||
$scope.inserted =
|
||||
name: ''
|
||||
$scope.components.push($scope.inserted)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Removes the newly inserted but not saved component / Cancel the current component modification
|
||||
# @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
|
||||
# @param index {number} component index in the $scope.components array
|
||||
##
|
||||
$scope.cancelComponent = (rowform, index) ->
|
||||
if $scope.components[index].id?
|
||||
rowform.$cancel()
|
||||
else
|
||||
$scope.components.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Saves a new theme / Update an existing theme to the server (form validation callback)
|
||||
# @param data {Object} theme name
|
||||
# @param [data] {number} theme id, in case of update
|
||||
##
|
||||
$scope.saveTheme = (data, id) ->
|
||||
if id?
|
||||
Theme.update {id: id}, data
|
||||
else
|
||||
Theme.save data, (resp)->
|
||||
$scope.themes[$scope.themes.length-1].id = resp.id
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Deletes the theme at the specified index
|
||||
# @param index {number} theme index in the $scope.themes array
|
||||
##
|
||||
$scope.removeTheme = (index) ->
|
||||
Theme.delete $scope.themes[index]
|
||||
$scope.themes.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Creates a new empty entry in the $scope.themes array
|
||||
##
|
||||
$scope.addTheme = ->
|
||||
$scope.inserted =
|
||||
name: ''
|
||||
$scope.themes.push($scope.inserted)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Removes the newly inserted but not saved theme / Cancel the current theme modification
|
||||
# @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
|
||||
# @param index {number} theme index in the $scope.themes array
|
||||
##
|
||||
$scope.cancelTheme = (rowform, index) ->
|
||||
if $scope.themes[index].id?
|
||||
rowform.$cancel()
|
||||
else
|
||||
$scope.themes.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Saves a new licence / Update an existing licence to the server (form validation callback)
|
||||
# @param data {Object} licence name and description
|
||||
# @param [data] {number} licence id, in case of update
|
||||
##
|
||||
$scope.saveLicence = (data, id) ->
|
||||
if id?
|
||||
Licence.update {id: id}, data
|
||||
else
|
||||
Licence.save data, (resp)->
|
||||
$scope.licences[$scope.licences.length-1].id = resp.id
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Deletes the licence at the specified index
|
||||
# @param index {number} licence index in the $scope.licences array
|
||||
##
|
||||
$scope.removeLicence = (index) ->
|
||||
Licence.delete $scope.licences[index]
|
||||
$scope.licences.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Creates a new empty entry in the $scope.licences array
|
||||
##
|
||||
$scope.addLicence = ->
|
||||
$scope.inserted =
|
||||
name: ''
|
||||
description: ''
|
||||
$scope.licences.push($scope.inserted)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Removes the newly inserted but not saved licence / Cancel the current licence modification
|
||||
# @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
|
||||
# @param index {number} licence index in the $scope.licences array
|
||||
##
|
||||
$scope.cancelLicence = (rowform, index) ->
|
||||
if $scope.licences[index].id?
|
||||
rowform.$cancel()
|
||||
else
|
||||
$scope.licences.splice(index, 1)
|
||||
]
|
||||
|
||||
|
330
app/assets/javascripts/controllers/application.coffee.erb
Normal file
@ -0,0 +1,330 @@
|
||||
'use strict'
|
||||
|
||||
Application.Controllers.controller 'ApplicationController', ["$rootScope", "$scope", "Session", "AuthService", "Auth", "$modal", "$state", 'growl', 'Notification', '$interval', ($rootScope, $scope, Session, AuthService, Auth, $modal, $state, growl, Notification, $interval) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# User's notifications will get refreshed every 30s
|
||||
NOTIFICATIONS_CHECK_PERIOD = 30000
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
##
|
||||
# Set the current user to the provided value and initialize the session
|
||||
# @param user {Object} Rails/Devise user
|
||||
##
|
||||
$scope.setCurrentUser = (user) ->
|
||||
$scope.currentUser = user
|
||||
Session.create(user);
|
||||
getNotifications()
|
||||
|
||||
|
||||
##
|
||||
# Login callback
|
||||
# @param e {Object} jQuery event
|
||||
# @param callback {function}
|
||||
##
|
||||
$scope.login = (e, callback) ->
|
||||
e.preventDefault() if e
|
||||
openLoginModal null, null, callback
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Logout callback
|
||||
# @param e {Object} jQuery event
|
||||
##
|
||||
$scope.logout = (e) ->
|
||||
e.preventDefault()
|
||||
Auth.logout().then (oldUser) ->
|
||||
# console.log(oldUser.name + " you're signed out now.");
|
||||
Session.destroy()
|
||||
$scope.currentUser = null
|
||||
$rootScope.toCheckNotifications = false
|
||||
$scope.notifications = []
|
||||
$state.go('app.public.home')
|
||||
, (error) ->
|
||||
# An error occurred logging out.
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Open the modal window allowing the user to create an account.
|
||||
# @param e {Object} jQuery event
|
||||
##
|
||||
$scope.signup = (e) ->
|
||||
e.preventDefault() if e
|
||||
|
||||
$modal.open
|
||||
templateUrl: '<%= asset_path "shared/signupModal.html" %>'
|
||||
size: 'md'
|
||||
controller: ['$scope', '$modalInstance', 'Group', ($scope, $modalInstance, Group) ->
|
||||
# default parameters for the date picker in the account creation modal
|
||||
$scope.datePicker =
|
||||
format: 'dd/MM/yyyy'
|
||||
opened: false
|
||||
options:
|
||||
startingDay: 1
|
||||
|
||||
# callback to open the date picker (account creation modal)
|
||||
$scope.openDatePicker = ($event) ->
|
||||
$event.preventDefault()
|
||||
$event.stopPropagation()
|
||||
$scope.datePicker.opened = true
|
||||
|
||||
# retrieve the groups (standard, student ...)
|
||||
Group.query (groups) ->
|
||||
$scope.groups = groups
|
||||
|
||||
# default user's parameters
|
||||
$scope.user =
|
||||
is_allow_contact: true
|
||||
|
||||
# Errors display
|
||||
$scope.alerts = []
|
||||
$scope.closeAlert = (index) ->
|
||||
$scope.alerts.splice(index, 1)
|
||||
|
||||
# callback for form validation
|
||||
$scope.ok = ->
|
||||
# try to create the account
|
||||
$scope.alerts = []
|
||||
Auth.register($scope.user).then (user) ->
|
||||
# creation successful
|
||||
$modalInstance.close(user)
|
||||
, (error) ->
|
||||
# creation failed...
|
||||
angular.forEach error.data.errors, (v, k) ->
|
||||
angular.forEach v, (err) ->
|
||||
$scope.alerts.push
|
||||
msg: k+': '+err
|
||||
type: 'danger'
|
||||
]
|
||||
.result['finally'](null).then (user) ->
|
||||
# when the account was created succesfully, set the session to the newly created account
|
||||
$scope.setCurrentUser(user)
|
||||
|
||||
|
||||
##
|
||||
# Open the modal window allowing the user to change his password.
|
||||
# @param token {string} security token for password changing. The user should have recieved it by mail
|
||||
##
|
||||
$scope.editPassword = (token) ->
|
||||
$modal.open
|
||||
templateUrl: '<%= asset_path "shared/passwordEditModal.html" %>'
|
||||
size: 'md'
|
||||
controller: ['$scope', '$modalInstance', '$http', ($scope, $modalInstance, $http) ->
|
||||
$scope.user =
|
||||
reset_password_token: token
|
||||
$scope.alerts = []
|
||||
$scope.closeAlert = (index) ->
|
||||
$scope.alerts.splice(index, 1)
|
||||
|
||||
$scope.changePassword = ->
|
||||
$scope.alerts = []
|
||||
$http.put('/users/password.json', {user: $scope.user}).success (data) ->
|
||||
$modalInstance.close()
|
||||
.error (data) ->
|
||||
angular.forEach data.errors, (v, k) ->
|
||||
angular.forEach v, (err) ->
|
||||
$scope.alerts.push
|
||||
msg: k+': '+err
|
||||
type: 'danger'
|
||||
]
|
||||
.result['finally'](null).then (user) ->
|
||||
growl.addInfoMessage('Votre mot de passe a bien été modifié.')
|
||||
Auth.login().then (user) ->
|
||||
$scope.setCurrentUser(user)
|
||||
, (error) ->
|
||||
# Authentication failed...
|
||||
|
||||
|
||||
##
|
||||
# Compact/Expend the width of the left navigation bar
|
||||
# @param e {Object} jQuery event object
|
||||
##
|
||||
$scope.toggleNavSize = (event) ->
|
||||
if typeof event == 'undefined'
|
||||
console.error '[applicationController::toggleNavSize] Missing event parameter'
|
||||
return
|
||||
|
||||
toggler = $(event.target)
|
||||
toggler = toggler.closest('[data-toggle^="class"]') unless toggler.data('toggle')
|
||||
|
||||
$class = toggler.data()['toggle']
|
||||
$target = toggler.data('target') or toggler.attr('data-link')
|
||||
|
||||
if $class
|
||||
$tmp = $class.split(':')[1]
|
||||
$classes = $tmp.split(',') if $tmp
|
||||
|
||||
if $target
|
||||
$targets = $target.split(',')
|
||||
|
||||
if $classes and $classes.length
|
||||
$.each $targets, ( index, value ) ->
|
||||
if $classes[index].indexOf( '*' ) != -1
|
||||
patt = new RegExp( '\\s'
|
||||
+ $classes[index].replace( /\*/g, '[A-Za-z0-9-_]+' ).split( ' ' ).join( '\\s|\\s' )
|
||||
+ '\\s', 'g' )
|
||||
$(toggler).each ( i, it ) ->
|
||||
cn = ' ' + it.className + ' '
|
||||
while patt.test( cn )
|
||||
cn = cn.replace( patt, ' ' )
|
||||
it.className = $.trim( cn )
|
||||
|
||||
($targets[index] !='#') and $($targets[index]).toggleClass($classes[index]) or toggler.toggleClass($classes[index])
|
||||
|
||||
toggler.toggleClass('active')
|
||||
return
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
# try to retrieve any currently logged user
|
||||
Auth.login().then (user) ->
|
||||
$scope.setCurrentUser(user)
|
||||
, (error) ->
|
||||
# Authentication failed...
|
||||
$rootScope.toCheckNotifications = false
|
||||
|
||||
# bind to the $stateChangeStart event (AngularJS/UI-Router)
|
||||
$rootScope.$on '$stateChangeStart', (event, toState, toParams, fromState, fromParams) ->
|
||||
return unless toState.data
|
||||
|
||||
authorizedRoles = toState.data.authorizedRoles
|
||||
unless AuthService.isAuthorized(authorizedRoles)
|
||||
event.preventDefault()
|
||||
if AuthService.isAuthenticated()
|
||||
# user is not allowed
|
||||
console.log('user is not allowed')
|
||||
else
|
||||
# user is not logged in
|
||||
openLoginModal(toState, toParams)
|
||||
|
||||
|
||||
# shorthands
|
||||
$scope.isAuthenticated = Auth.isAuthenticated;
|
||||
$scope.isAuthorized = AuthService.isAuthorized;
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Retreive once the notifications from the server and display a message popup for each new one.
|
||||
# Then, periodically check for new notifications.
|
||||
##
|
||||
getNotifications = ->
|
||||
$rootScope.toCheckNotifications = true
|
||||
unless $rootScope.checkNotificationsIsInit or !$scope.currentUser
|
||||
$scope.notifications = Notification.query {is_read: false}
|
||||
$scope.$watch 'notifications', (newValue, oldValue) ->
|
||||
diff = []
|
||||
angular.forEach newValue, (value) ->
|
||||
find = false
|
||||
for i in [0..oldValue.length] by 1
|
||||
if oldValue[i] and (value.id is oldValue[i].id)
|
||||
find = true
|
||||
break
|
||||
|
||||
unless find
|
||||
diff.push(value)
|
||||
|
||||
|
||||
angular.forEach diff, (notification, key) ->
|
||||
growl.addInfoMessage(notification.message.description)
|
||||
|
||||
, true
|
||||
|
||||
checkNotifications = ->
|
||||
if $rootScope.toCheckNotifications
|
||||
Notification.query({is_read: false}).$promise.then (data) ->
|
||||
$scope.notifications = data;
|
||||
|
||||
$interval(checkNotifications, NOTIFICATIONS_CHECK_PERIOD)
|
||||
$rootScope.checkNotificationsIsInit = true
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Open the modal window allowing the user to log in.
|
||||
##
|
||||
openLoginModal = (toState, toParams, callback) ->
|
||||
$modal.open
|
||||
templateUrl: '<%= asset_path "shared/deviseModal.html" %>'
|
||||
size: 'sm'
|
||||
controller: ['$scope', '$modalInstance', ($scope, $modalInstance) ->
|
||||
user = $scope.user = {}
|
||||
$scope.login = () ->
|
||||
Auth.login(user).then (user) ->
|
||||
# Authentification succeeded ...
|
||||
$modalInstance.close(user)
|
||||
if callback and typeof callback is "function"
|
||||
callback(user)
|
||||
, (error) ->
|
||||
# Authentication failed...
|
||||
$scope.alerts = []
|
||||
$scope.alerts.push
|
||||
msg: 'E-mail ou mot de passe incorrect.'
|
||||
type: 'danger'
|
||||
|
||||
# handle modal behaviors. The provided reason will be used to define the following actions
|
||||
$scope.dismiss = ->
|
||||
$modalInstance.dismiss('cancel')
|
||||
|
||||
$scope.openSignup = (e) ->
|
||||
e.preventDefault()
|
||||
$modalInstance.dismiss('signup')
|
||||
|
||||
$scope.openResetPassword = (e) ->
|
||||
e.preventDefault()
|
||||
$modalInstance.dismiss('resetPassword')
|
||||
]
|
||||
|
||||
# what to do when the modal is closed
|
||||
.result['finally'](null).then (user) ->
|
||||
# authentification succeeded, set the session, gather the notifications and redirect
|
||||
$scope.setCurrentUser(user)
|
||||
|
||||
if toState isnt null and toParams isnt null
|
||||
$state.go(toState, toParams)
|
||||
|
||||
, (reason) ->
|
||||
# authentification did not ended successfully
|
||||
if reason is 'signup'
|
||||
# open signup modal
|
||||
$scope.signup()
|
||||
else if reason is 'resetPassword'
|
||||
# open the 'reset password' modal
|
||||
$modal.open
|
||||
templateUrl: '<%= asset_path "shared/passwordNewModal.html" %>'
|
||||
size: 'sm'
|
||||
controller: ['$scope', '$modalInstance', '$http', ($scope, $modalInstance, $http) ->
|
||||
$scope.user = {email: ''}
|
||||
$scope.sendReset = () ->
|
||||
$scope.alerts = []
|
||||
$http.post('/users/password.json', {user: $scope.user}).success ->
|
||||
$modalInstance.close()
|
||||
.error ->
|
||||
$scope.alerts.push
|
||||
msg: "Votre adresse email n'existe pas."
|
||||
type: 'danger'
|
||||
|
||||
]
|
||||
.result['finally'](null).then ->
|
||||
growl.addInfoMessage('Vous allez recevoir sous quelques minutes un e-mail vous indiquant comment réinitialiser votre mot de passe.')
|
||||
|
||||
# otherwise the user just closed the modal
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
43
app/assets/javascripts/controllers/dashboard.coffee
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict'
|
||||
|
||||
##
|
||||
# Controller used on the private projects listing page (my dashboard/projects)
|
||||
##
|
||||
Application.Controllers.controller "dashboardProjectsController", ["$scope", 'Member', ($scope, Member) ->
|
||||
|
||||
## Current user's profile
|
||||
$scope.user = Member.get {id: $scope.currentUser.id}
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used on the personal trainings page (my dashboard/trainings)
|
||||
##
|
||||
Application.Controllers.controller "dashboardTrainingsController", ["$scope", 'Member', ($scope, Member) ->
|
||||
|
||||
## Current user's profile
|
||||
$scope.user = Member.get {id: $scope.currentUser.id}
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used on the private events page (my dashboard/events)
|
||||
##
|
||||
Application.Controllers.controller "dashboardEventsController", ["$scope", 'Member', ($scope, Member) ->
|
||||
|
||||
## Current user's profile
|
||||
$scope.user = Member.get {id: $scope.currentUser.id}
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used on the personal invoices listing page (my dashboard/invoices)
|
||||
##
|
||||
Application.Controllers.controller "dashboardInvoicesController", ["$scope", 'Member', ($scope, Member) ->
|
||||
|
||||
## Current user's profile
|
||||
$scope.user = Member.get {id: $scope.currentUser.id}
|
||||
]
|
120
app/assets/javascripts/controllers/events.coffee.erb
Normal file
@ -0,0 +1,120 @@
|
||||
'use strict'
|
||||
|
||||
Application.Controllers.controller "eventsController", ["$scope", "$state", 'Event', ($scope, $state, Event) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# Number of events added to the page when the user clicks on 'load next events'
|
||||
EVENTS_PER_PAGE = 12
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## The events displayed on the page
|
||||
$scope.events = []
|
||||
|
||||
## By default, the pagination mode is activated to limit the page size
|
||||
$scope.paginateActive = true
|
||||
|
||||
## The currently displayed page number
|
||||
$scope.page = 1
|
||||
|
||||
##
|
||||
# Adds EVENTS_PER_PAGE events to the bottom of the page, grouped by month
|
||||
##
|
||||
$scope.loadMoreEvents = ->
|
||||
Event.query {page: $scope.page}, (data) ->
|
||||
$scope.events = $scope.events.concat data
|
||||
if data.length > 0
|
||||
$scope.paginateActive = false if ($scope.page-2)*EVENTS_PER_PAGE+data.length >= data[0].nb_total_events
|
||||
|
||||
$scope.eventsGroupByMonth = _.groupBy($scope.events, (obj) ->
|
||||
_.map ['month', 'year'], (key, value) -> obj[key]
|
||||
)
|
||||
$scope.monthOrder = _.sortBy _.keys($scope.eventsGroupByMonth), (k)->
|
||||
monthYearArray = k.split(',')
|
||||
date = new Date()
|
||||
date.setMonth(monthYearArray[0])
|
||||
date.setYear(monthYearArray[1])
|
||||
return -date.getTime()
|
||||
else
|
||||
$scope.paginateActive = false
|
||||
$scope.page += 1
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Callback to redirect the user to the specified event page
|
||||
# @param event {{id:number}}
|
||||
##
|
||||
$scope.showEvent = (event) ->
|
||||
$state.go('app.public.events_show', {id: event.id})
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
$scope.loadMoreEvents()
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Application.Controllers.controller "showEventController", ["$scope", "$state", "$stateParams", "Event", '$modal', 'Member', ($scope, $state, $stateParams, Event, $modal, Member) ->
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## current event details
|
||||
$scope.event = {}
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Callback to delete the provided event (admins only)
|
||||
# @param event {$resource} angular's Event $resource
|
||||
##
|
||||
$scope.deleteEvent = (event) ->
|
||||
event.$delete ->
|
||||
$state.go('app.public.events_list')
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
|
||||
# get the details for the current event (event's id is recovered from the current URL)
|
||||
Event.get {id: $stateParams.id}
|
||||
, (data) ->
|
||||
$scope.event = data
|
||||
if !$scope.event.reduced_amount
|
||||
$scope.event.reduced_amount = 0
|
||||
return
|
||||
, ->
|
||||
$state.go('app.public.events_list')
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
||||
|
60
app/assets/javascripts/controllers/home.coffee
Normal file
@ -0,0 +1,60 @@
|
||||
'use strict'
|
||||
|
||||
Application.Controllers.controller "homeController", ['$scope', '$stateParams', 'Member', 'Twitter', 'Project', 'Event', ($scope, $stateParams, Member, Twitter, Project, Event) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# The 4 last users will be displayed on the home page
|
||||
LAST_MEMBERS_LIMIT = 4
|
||||
|
||||
# Only the last tweet is shown
|
||||
LAST_TWEETS_LIMIT = 1
|
||||
|
||||
# The 3 closest events are shown
|
||||
LAST_EVENTS_LIMIT = 3
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## The last registered members who confirmed their addresses
|
||||
$scope.last_members = []
|
||||
|
||||
## The last tweets from the Fablab official twitter account
|
||||
$scope.last_tweets = []
|
||||
|
||||
## The last projects published/documented on the plateform
|
||||
$scope.last_projects = []
|
||||
|
||||
## The closest upcoming events
|
||||
$scope.upcoming_events = []
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
# display the reset password dialog if the parameter was provided
|
||||
if $stateParams.reset_password_token
|
||||
$scope.$parent.editPassword($stateParams.reset_password_token)
|
||||
|
||||
# initialize the homepage data
|
||||
Member.lastSubscribed {limit: LAST_MEMBERS_LIMIT}, (members) ->
|
||||
$scope.last_members = members
|
||||
Twitter.query {limit: LAST_TWEETS_LIMIT}, (tweets) ->
|
||||
$scope.last_tweets = tweets
|
||||
Project.lastPublished (projects) ->
|
||||
$scope.last_projects = projects
|
||||
Event.upcoming {limit: LAST_EVENTS_LIMIT}, (events) ->
|
||||
$scope.upcoming_events = events
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
164
app/assets/javascripts/controllers/machines.coffee.erb
Normal file
@ -0,0 +1,164 @@
|
||||
'use strict'
|
||||
|
||||
### COMMON CODE ###
|
||||
|
||||
##
|
||||
# Provides a set of common callback methods to the $scope parameter. These methods are used
|
||||
# in the various machines' admin controllers.
|
||||
#
|
||||
# Provides :
|
||||
# - $scope.submited(content)
|
||||
# - $scope.cancel()
|
||||
# - $scope.fileinputClass(v)
|
||||
# - $scope.addFile()
|
||||
# - $scope.deleteFile(file)
|
||||
#
|
||||
# Requires :
|
||||
# - $scope.machine.machine_files_attributes = []
|
||||
# - $state (Ui-Router) [ 'app.public.machines_list' ]
|
||||
##
|
||||
class MachinesController
|
||||
constructor: ($scope, $state)->
|
||||
##
|
||||
# For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
# Intended to be the callback when the upload is done: any raised error will be stacked in the
|
||||
# $scope.alerts array. If everything goes fine, the user is redirected to the machines list.
|
||||
# @param content {Object} JSON - The upload's result
|
||||
##
|
||||
$scope.submited = (content) ->
|
||||
if !content.id?
|
||||
$scope.alerts = []
|
||||
angular.forEach content, (v, k)->
|
||||
angular.forEach v, (err)->
|
||||
$scope.alerts.push
|
||||
msg: k+': '+err
|
||||
type: 'danger'
|
||||
else
|
||||
$state.go('app.public.machines_list')
|
||||
|
||||
##
|
||||
# Changes the current user's view, redirecting him to the machines list
|
||||
##
|
||||
$scope.cancel = ->
|
||||
$state.go('app.public.machines_list')
|
||||
|
||||
##
|
||||
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
# The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
# @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
|
||||
##
|
||||
$scope.fileinputClass = (v)->
|
||||
if v
|
||||
'fileinput-exists'
|
||||
else
|
||||
'fileinput-new'
|
||||
|
||||
##
|
||||
# This will create a single new empty entry into the machine attachements list.
|
||||
##
|
||||
$scope.addFile = ->
|
||||
$scope.machine.machine_files_attributes.push {}
|
||||
|
||||
##
|
||||
# This will remove the given file from the machine attachements list. If the file was previously uploaded
|
||||
# to the server, it will be marked for deletion on the server. Otherwise, it will be simply truncated from
|
||||
# the attachements array.
|
||||
# @param file {Object} the file to delete
|
||||
##
|
||||
$scope.deleteFile = (file) ->
|
||||
index = $scope.machine.machine_files_attributes.indexOf(file)
|
||||
if file.id?
|
||||
file._destroy = true
|
||||
else
|
||||
$scope.machine.machine_files_attributes.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the public listing page, allowing everyone to see the list of machines
|
||||
##
|
||||
Application.Controllers.controller "machinesController", ["$scope", "$state", 'Machine', '$modal', ($scope, $state, Machine, $modal) ->
|
||||
|
||||
## Retrieve the list of machines
|
||||
$scope.machines = Machine.query()
|
||||
|
||||
##
|
||||
# Redirect the user to the machine details page
|
||||
##
|
||||
$scope.showMachine = (machine) ->
|
||||
$state.go('app.public.machines_show', {id: machine.slug})
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the machine creation page (admin)
|
||||
##
|
||||
Application.Controllers.controller "newMachineController", ["$scope", "$state", 'CSRF', ($scope, $state, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/machines/"
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = "post"
|
||||
|
||||
## default machine parameters
|
||||
$scope.machine =
|
||||
machine_files_attributes: []
|
||||
|
||||
## Using the MachinesController
|
||||
new MachinesController($scope, $state)
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the machine edition page (admin)
|
||||
##
|
||||
Application.Controllers.controller "editMachineController", ["$scope", "$state", '$stateParams', 'Machine', 'CSRF', ($scope, $state, $stateParams, Machine, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/machines/" + $stateParams.id
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = "put"
|
||||
|
||||
## Retrieve the details for the machine id in the URL, if an error occurs redirect the user to the machines list
|
||||
$scope.machine = Machine.get {id: $stateParams.id}
|
||||
, ->
|
||||
return
|
||||
, ->
|
||||
$state.go('app.public.machines_list')
|
||||
|
||||
## Using the MachinesController
|
||||
new MachinesController($scope, $state)
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the machine details page (public)
|
||||
##
|
||||
Application.Controllers.controller "showMachineController", ['$scope', '$state', '$modal', '$stateParams', 'Machine', ($scope, $state, $modal, $stateParams, Machine) ->
|
||||
|
||||
## Retrieve the details for the machine id in the URL, if an error occurs redirect the user to the machines list
|
||||
$scope.machine = Machine.get {id: $stateParams.id}
|
||||
, ->
|
||||
return
|
||||
, ->
|
||||
$state.go('app.public.machines_list')
|
||||
|
||||
##
|
||||
# Callback to delete the current machine (admins only)
|
||||
##
|
||||
$scope.delete = (machine) ->
|
||||
# check the permissions
|
||||
if $scope.currentUser.role isnt 'admin'
|
||||
console.error 'Unauthorized operation'
|
||||
else
|
||||
# delete the machine then redirect to the machines listing
|
||||
machine.$delete ->
|
||||
$state.go('app.public.machines_list')
|
||||
]
|
57
app/assets/javascripts/controllers/main_nav.coffee.erb
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict'
|
||||
|
||||
##
|
||||
# Navigation controller. List the links availables in the left navigation pane and their icon.
|
||||
##
|
||||
Application.Controllers.controller "mainNavController", ["$scope", "$location", "$cookies", ($scope, $location, $cookies) ->
|
||||
|
||||
## Common links (public application)
|
||||
$scope.navLinks = [
|
||||
{
|
||||
state: 'app.public.home'
|
||||
linkText: 'Accueil'
|
||||
linkIcon: 'home'
|
||||
}
|
||||
|
||||
{
|
||||
state: 'app.public.machines_list'
|
||||
linkText: 'Liste des machines'
|
||||
linkIcon: 'gears'
|
||||
}
|
||||
{
|
||||
state: 'app.public.events_list'
|
||||
linkText: 'Liste des stages et ateliers'
|
||||
linkIcon: 'tags'
|
||||
}
|
||||
{
|
||||
state: 'app.public.projects_list'
|
||||
linkText: 'Galerie de projets'
|
||||
linkIcon: 'th'
|
||||
}
|
||||
]
|
||||
|
||||
## Admin links (backoffice application)
|
||||
$scope.adminNavLinks = [
|
||||
{
|
||||
state: 'app.admin.members'
|
||||
linkText: 'Suivi utilisateurs'
|
||||
linkIcon: 'users'
|
||||
}
|
||||
{
|
||||
state: 'app.admin.events'
|
||||
linkText: 'Suivi stages et ateliers'
|
||||
linkIcon: 'tags'
|
||||
}
|
||||
{
|
||||
state: 'app.public.machines_list'
|
||||
linkText: 'Gérer les machines'
|
||||
linkIcon: 'cogs'
|
||||
}
|
||||
{
|
||||
state: 'app.admin.project_elements'
|
||||
linkText: 'Gérer les éléments Projets'
|
||||
linkIcon: 'tasks'
|
||||
}
|
||||
]
|
||||
|
||||
]
|
109
app/assets/javascripts/controllers/members.coffee
Normal file
@ -0,0 +1,109 @@
|
||||
'use strict'
|
||||
|
||||
##
|
||||
# Controller used in the members listing page
|
||||
##
|
||||
Application.Controllers.controller "membersController", ["$scope", "$state", 'Member', ($scope, $state, Member) ->
|
||||
|
||||
## members list
|
||||
$scope.members = Member.query()
|
||||
|
||||
## Merbers ordering/sorting. Default: not sorted
|
||||
$scope.orderMember = null
|
||||
|
||||
##
|
||||
# Change the members ordering criterion to the one provided
|
||||
# @param orderBy {string} ordering criterion
|
||||
##
|
||||
$scope.setOrderMember = (orderBy)->
|
||||
if $scope.orderMember == orderBy
|
||||
$scope.orderMember = '-'+orderBy
|
||||
else
|
||||
$scope.orderMember = orderBy
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used when editing the current user's profile
|
||||
##
|
||||
Application.Controllers.controller "editProfileController", ["$scope", "$state", "Member", "Auth", 'growl', 'dialogs', 'CSRF', ($scope, $state, Member, Auth, growl, dialogs, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/members/" + $scope.currentUser.id
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'patch'
|
||||
|
||||
## Current user's profile
|
||||
$scope.user = Member.get {id: $scope.currentUser.id}
|
||||
|
||||
## Angular-Bootstrap datepicker configuration for birthday
|
||||
$scope.datePicker =
|
||||
format: 'dd/MM/yyyy'
|
||||
opened: false # default: datePicker is not shown
|
||||
options:
|
||||
startingDay: 1 # France: the week starts on monday
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Callback to diplay the datepicker as a dropdown when clicking on the input field
|
||||
# @param $event {Object} jQuery event object
|
||||
##
|
||||
$scope.openDatePicker = ($event) ->
|
||||
$event.preventDefault()
|
||||
$event.stopPropagation()
|
||||
$scope.datePicker.opened = true
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
# Intended to be the callback when the upload is done: any raised error will be stacked in the
|
||||
# $scope.alerts array. If everything goes fine, the user's profile is updated and the user is
|
||||
# redirected to the home page
|
||||
# @param content {Object} JSON - The upload's result
|
||||
##
|
||||
$scope.submited = (content) ->
|
||||
if !content.id?
|
||||
$scope.alerts = []
|
||||
angular.forEach content, (v, k)->
|
||||
angular.forEach v, (err)->
|
||||
$scope.alerts.push
|
||||
msg: k+': '+err,
|
||||
type: 'danger'
|
||||
else
|
||||
$scope.currentUser.profile.user_avatar = content.profile.user_avatar
|
||||
Auth._currentUser.profile.user_avatar = content.profile.user_avatar
|
||||
$scope.currentUser.name = content.name
|
||||
Auth._currentUser.name = content.name
|
||||
$scope.currentUser = content
|
||||
Auth._currentUser = content
|
||||
$state.go('app.public.home')
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
# The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
# @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
|
||||
##
|
||||
$scope.fileinputClass = (v)->
|
||||
if v
|
||||
'fileinput-exists'
|
||||
else
|
||||
'fileinput-new'
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used on the public user's profile page (seeing another user's profile)
|
||||
##
|
||||
Application.Controllers.controller "showProfileController", ["$scope", "$stateParams", 'Member', ($scope, $stateParams, Member) ->
|
||||
|
||||
## Selected user's profile (id from the current URL)
|
||||
$scope.user = Member.get {id: $stateParams.id}
|
||||
]
|
87
app/assets/javascripts/controllers/notifications.coffee
Normal file
@ -0,0 +1,87 @@
|
||||
'use strict'
|
||||
|
||||
##
|
||||
# Controller used in notifications page
|
||||
# inherits $scope.$parent.notifications (unread notifications) from ApplicationController
|
||||
##
|
||||
Application.Controllers.controller "notificationsController", ["$scope", 'Notification', ($scope, Notification) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# Number of notifications added to the page when the user clicks on 'load next notifications'
|
||||
NOTIFICATIONS_PER_PAGE = 15
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## Array containg the archived notifications (already read)
|
||||
$scope.notificationsRead = []
|
||||
|
||||
## By default, the pagination mode is activated to limit the page size
|
||||
$scope.paginateActive = true
|
||||
|
||||
## The currently displayed page number
|
||||
$scope.page = 1
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Mark the provided notification as read, updating its status on the server and moving it
|
||||
# to the already read notifications list.
|
||||
# @param notification {{id:number}} the notification to mark as read
|
||||
# @param e {Object} jQuery event object
|
||||
##
|
||||
$scope.markAsRead = (notification, e) ->
|
||||
e.preventDefault()
|
||||
Notification.update {id: notification.id},
|
||||
id: notification.id
|
||||
is_read: true
|
||||
, ->
|
||||
index = $scope.$parent.notifications.indexOf(notification)
|
||||
$scope.$parent.notifications.splice(index,1)
|
||||
$scope.notificationsRead.push notification
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Mark every unread notifications as read and move them for the unread list to to read array.
|
||||
##
|
||||
$scope.markAllAsRead = ->
|
||||
Notification.update {}
|
||||
, -> # success
|
||||
angular.forEach $scope.$parent.notifications, (n)->
|
||||
$scope.notificationsRead.push n
|
||||
|
||||
$scope.$parent.notifications.splice(0, $scope.$parent.notifications.length)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Request the server to retrieve the next undisplayed notifications and add them
|
||||
# to the archived notifications list.
|
||||
##
|
||||
$scope.addMoreNotificationsReaded = ->
|
||||
Notification.query {is_read: true, page: $scope.page}, (notifications) ->
|
||||
$scope.notificationsRead = $scope.notificationsRead.concat notifications
|
||||
$scope.paginateActive = false if notifications.length < NOTIFICATIONS_PER_PAGE
|
||||
|
||||
$scope.page += 1
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
$scope.addMoreNotificationsReaded()
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
358
app/assets/javascripts/controllers/projects.coffee
Normal file
@ -0,0 +1,358 @@
|
||||
'use strict'
|
||||
|
||||
### COMMON CODE ###
|
||||
|
||||
##
|
||||
# Provides a set of common properties and methods to the $scope parameter. They are used
|
||||
# in the various projects' admin controllers.
|
||||
#
|
||||
# Provides :
|
||||
# - $scope.machines = [{Machine}]
|
||||
# - $scope.components = [{Component}]
|
||||
# - $scope.themes = [{Theme}]
|
||||
# - $scope.licences = [{Licence}]
|
||||
# - $scope.submited(content)
|
||||
# - $scope.cancel()
|
||||
# - $scope.addFile()
|
||||
# - $scope.deleteFile(file)
|
||||
# - $scope.addStep()
|
||||
# - $scope.deleteStep(step)
|
||||
#
|
||||
# Requires :
|
||||
# - $scope.project.project_caos_attributes = []
|
||||
# - $scope.project.project_steps_attributes = []
|
||||
# - $state (Ui-Router) [ 'app.public.projects_show', 'app.public.projects_list' ]
|
||||
##
|
||||
class ProjectsController
|
||||
constructor: ($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document)->
|
||||
|
||||
## Retrieve the list of machines from the server
|
||||
Machine.query().$promise.then (data)->
|
||||
$scope.machines = data.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
## Retrieve the list of components from the server
|
||||
Component.query().$promise.then (data)->
|
||||
$scope.components = data.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
## Retrieve the list of themes from the server
|
||||
Theme.query().$promise.then (data)->
|
||||
$scope.themes = data.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
## Retrieve the list of licences from the server
|
||||
Licence.query().$promise.then (data)->
|
||||
$scope.licences = data.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with ngUpload (https://github.com/twilson63/ngUpload).
|
||||
# Intended to be the callback when an upload is done: any raised error will be stacked in the
|
||||
# $scope.alerts array. If everything goes fine, the user is redirected to the project page.
|
||||
# @param content {Object} JSON - The upload's result
|
||||
##
|
||||
$scope.submited = (content) ->
|
||||
if !content.id?
|
||||
$scope.alerts = []
|
||||
angular.forEach content, (v, k)->
|
||||
angular.forEach v, (err)->
|
||||
$scope.alerts.push
|
||||
msg: k+': '+err
|
||||
type: 'danger'
|
||||
# using https://github.com/oblador/angular-scroll
|
||||
$('section[ui-view=main]').scrollTop(0, 200)
|
||||
return
|
||||
else
|
||||
$state.go('app.public.projects_show', {id: content.id})
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Changes the user's view to the projects list page
|
||||
##
|
||||
$scope.cancel = ->
|
||||
$state.go('app.public.projects_list')
|
||||
|
||||
|
||||
|
||||
##
|
||||
# For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
# The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
# @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
|
||||
##
|
||||
$scope.fileinputClass = (v)->
|
||||
if v
|
||||
'fileinput-exists'
|
||||
else
|
||||
'fileinput-new'
|
||||
|
||||
|
||||
|
||||
##
|
||||
# This will create a single new empty entry into the project's CAO attachements list.
|
||||
##
|
||||
$scope.addFile = ->
|
||||
$scope.project.project_caos_attributes.push {}
|
||||
|
||||
|
||||
|
||||
##
|
||||
# This will remove the given file from the project's CAO attachements list. If the file was previously uploaded
|
||||
# to the server, it will be marked for deletion on the server. Otherwise, it will be simply truncated from
|
||||
# the CAO attachements array.
|
||||
# @param file {Object} the file to delete
|
||||
##
|
||||
$scope.deleteFile = (file) ->
|
||||
index = $scope.project.project_caos_attributes.indexOf(file)
|
||||
if file.id?
|
||||
file._destroy = true
|
||||
else
|
||||
$scope.project.project_caos_attributes.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# This will create a single new empty entry into the project's steps list.
|
||||
##
|
||||
$scope.addStep = ->
|
||||
$scope.project.project_steps_attributes.push {}
|
||||
|
||||
|
||||
|
||||
##
|
||||
# This will remove the given stip from the project's steps list. If the step was previously saved
|
||||
# on the server, it will be marked for deletion for the next saving. Otherwise, it will be simply truncated from
|
||||
# the steps array.
|
||||
# @param file {Object} the file to delete
|
||||
##
|
||||
$scope.deleteStep = (step) ->
|
||||
index = $scope.project.project_steps_attributes.indexOf(step)
|
||||
if step.id?
|
||||
step._destroy = true
|
||||
else
|
||||
$scope.project.project_steps_attributes.splice(index, 1)
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used on projects listing page
|
||||
##
|
||||
Application.Controllers.controller "projectsController", ["$scope", "$state", 'Project', 'Machine', 'Theme', 'Component', ($scope, $state, Project, Machine, Theme, Component) ->
|
||||
|
||||
|
||||
|
||||
### PRIVATE STATIC CONSTANTS ###
|
||||
|
||||
# Number of notifications added to the page when the user clicks on 'load next notifications'
|
||||
PROJECTS_PER_PAGE = 12
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## list of projects to display
|
||||
$scope.projects = []
|
||||
|
||||
## list of machines / used for filtering
|
||||
$scope.machines = []
|
||||
|
||||
## list of themes / used for filtering
|
||||
$scope.themes = Theme.query()
|
||||
|
||||
## list of components / used for filtering
|
||||
$scope.components = Component.query()
|
||||
|
||||
## By default, the pagination mode is activated to limit the page size
|
||||
$scope.paginateActive = true
|
||||
|
||||
## The currently displayed page number
|
||||
$scope.page = 1
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Request the server to retrieve the next undisplayed projects and add them
|
||||
# to the local projects list.
|
||||
##
|
||||
$scope.loadMoreProjects = ->
|
||||
Project.query {page: $scope.page}, (projects) ->
|
||||
$scope.projects = $scope.projects.concat projects
|
||||
$scope.paginateActive = false if projects.length < PROJECTS_PER_PAGE
|
||||
|
||||
$scope.page += 1
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Callback to switch the user's view to the detailled project page
|
||||
# @param project {{slug:string}} The project to display
|
||||
##
|
||||
$scope.showProject = (project) ->
|
||||
$state.go('app.public.projects_show', {id: project.slug})
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Callback to delete the provided project. Then, the projects list page is refreshed (admins only)
|
||||
##
|
||||
$scope.delete = (project) ->
|
||||
# check the permissions
|
||||
if $scope.currentUser.role isnt 'admin'
|
||||
console.error 'Unauthorized operation'
|
||||
else
|
||||
# delete the project then refresh the projects list
|
||||
project.$delete ->
|
||||
$state.go('app.public.projects_list', {}, {reload: true})
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
Machine.query().$promise.then (data)->
|
||||
$scope.machines = data.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
$scope.loadMoreProjects()
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the project creation page
|
||||
##
|
||||
Application.Controllers.controller "newProjectController", ["$scope", "$state", 'Project', 'Machine', 'Member', 'Component', 'Theme', 'Licence', '$document', 'CSRF', ($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/projects/"
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'post'
|
||||
|
||||
## Button litteral text value
|
||||
$scope.submitName = 'Enregistrer comme brouillon'
|
||||
|
||||
## Default project parameters
|
||||
$scope.project =
|
||||
project_steps_attributes: []
|
||||
project_caos_attributes: []
|
||||
|
||||
## Other members list (project collaborators)
|
||||
Member.query().$promise.then (data)->
|
||||
$scope.members = data.filter (m) ->
|
||||
m.id != $scope.currentUser.id
|
||||
.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
## Using the ProjectsController
|
||||
new ProjectsController($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document)
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the project edition page
|
||||
##
|
||||
Application.Controllers.controller "editProjectController", ["$scope", "$state", '$stateParams', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'Licence', '$document', 'CSRF', ($scope, $state, $stateParams, Project, Machine, Member, Component, Theme, Licence, $document, CSRF) ->
|
||||
CSRF.setMetaTags()
|
||||
|
||||
## API URL where the form will be posted
|
||||
$scope.actionUrl = "/api/projects/" + $stateParams.id
|
||||
|
||||
## Form action on the above URL
|
||||
$scope.method = 'put'
|
||||
|
||||
## Button litteral text value
|
||||
$scope.submitName = 'Enregistrer'
|
||||
|
||||
## Retrieve the project's details, if an error occured, redirect the user to the projects list page
|
||||
$scope.project = Project.get {id: $stateParams.id}
|
||||
, -> # success
|
||||
return
|
||||
, -> # failed
|
||||
$state.go('app.public.projects_list')
|
||||
|
||||
## Other members list (project collaborators)
|
||||
Member.query().$promise.then (data)->
|
||||
$scope.members = data.filter (m) ->
|
||||
m.id != $scope.project.author_id
|
||||
.map (d) ->
|
||||
id: d.id
|
||||
name: d.name
|
||||
|
||||
## Using the ProjectsController
|
||||
new ProjectsController($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document)
|
||||
]
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Controller used in the public project's details page
|
||||
##
|
||||
Application.Controllers.controller "showProjectController", ["$scope", "$state", "$stateParams", "Project", '$location', ($scope, $state, $stateParams, Project, $location) ->
|
||||
|
||||
|
||||
|
||||
### PUBLIC SCOPE ###
|
||||
|
||||
## Will be set to true once the project details are loaded. Used to load the Disqus plugin at the right moment
|
||||
$scope.contentLoaded = false
|
||||
|
||||
## Store the project's details
|
||||
$scope.project = {}
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Test if the provided user has the edition rights on the current project
|
||||
# @param [user] {{id:number}} (optional) the user to check rights
|
||||
# @returns boolean
|
||||
##
|
||||
$scope.projectEditableBy = (user) ->
|
||||
return false if not user?
|
||||
return true if $scope.project.author_id == user.id
|
||||
canEdit = false
|
||||
angular.forEach $scope.project.project_users, (u)->
|
||||
canEdit = true if u.id == user.id and u.is_valid
|
||||
return canEdit
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
# Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
##
|
||||
initialize = ->
|
||||
## Retrieve the project content
|
||||
$scope.project = Project.get {id: $stateParams.id}
|
||||
, -> # success
|
||||
$scope.contentLoaded = true
|
||||
$scope.project_url = $location.absUrl()
|
||||
return
|
||||
, -> # failed, redirect the user to the projects listing
|
||||
$state.go('app.public.projects_list')
|
||||
|
||||
|
||||
|
||||
## !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize()
|
||||
]
|
37
app/assets/javascripts/directives/bs-jasny-fileinput.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
Application.Directives.directive('bsJasnyFileinput', [function(){
|
||||
return {
|
||||
require : ['ngModel'],
|
||||
link : function($scope, elm, attrs, requiredCtrls){
|
||||
var ngModelCtrl = requiredCtrls[0];
|
||||
var fileinput = elm.parents('[data-provides=fileinput]');
|
||||
var filetypeRegex = attrs.bsJasnyFileinput;
|
||||
fileinput.on('clear.bs.fileinput', function(e){
|
||||
if(ngModelCtrl){
|
||||
ngModelCtrl.$setViewValue(null);
|
||||
ngModelCtrl.$setPristine();
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
fileinput.on('change.bs.fileinput', function(e, files){
|
||||
if(ngModelCtrl){
|
||||
if(files){
|
||||
ngModelCtrl.$setViewValue(files.result);
|
||||
} else {
|
||||
ngModelCtrl.$setPristine();
|
||||
}
|
||||
|
||||
// TODO: ne marche pas pour filetype
|
||||
if (filetypeRegex) {
|
||||
if(files && typeof files.type !== "undefined" && files.type.match(new RegExp(filetypeRegex)))
|
||||
ngModelCtrl.$setValidity('filetype', true);
|
||||
else
|
||||
ngModelCtrl.$setValidity('filetype', false);
|
||||
}
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
}
|
||||
}]);
|
68
app/assets/javascripts/directives/directives.coffee
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict'
|
||||
|
||||
Application.Directives.directive 'fileread', [ ->
|
||||
{
|
||||
scope:
|
||||
fileread: "="
|
||||
|
||||
link: (scope, element, attributes) ->
|
||||
element.bind "change", (changeEvent) ->
|
||||
scope.$apply ->
|
||||
scope.fileread = changeEvent.target.files[0]
|
||||
}
|
||||
]
|
||||
|
||||
# This `bsHolder` angular directive is a workaround for
|
||||
# an incompatability between angular and the holder.js
|
||||
# image placeholder library.
|
||||
#
|
||||
# To use, simply define `bs-holder` on any element
|
||||
Application.Directives.directive 'bsHolder', [ ->
|
||||
{
|
||||
link: (scope, element, attrs) ->
|
||||
Holder.addTheme("icon", { background: "white", foreground: "#e9e9e9", size: 80, font: "FontAwesome"})
|
||||
.addTheme("avatar", { background: "#eeeeee", foreground: "#555555", size: 16, font: "FontAwesome"})
|
||||
.run(element[0])
|
||||
return
|
||||
}
|
||||
]
|
||||
|
||||
Application.Directives.directive 'match', [ ->
|
||||
{
|
||||
require: 'ngModel'
|
||||
restrict: 'A'
|
||||
scope:
|
||||
match: '='
|
||||
link: (scope, elem, attrs, ctrl) ->
|
||||
scope.$watch ->
|
||||
(ctrl.$pristine && angular.isUndefined(ctrl.$modelValue)) || scope.match == ctrl.$modelValue
|
||||
, (currentValue) ->
|
||||
ctrl.$setValidity('match', currentValue)
|
||||
}
|
||||
]
|
||||
|
||||
Application.Directives.directive 'publishProject', [ ->
|
||||
{
|
||||
restrict: 'A'
|
||||
link: (scope, elem, attrs, ctrl) ->
|
||||
elem.bind 'click', ($event)->
|
||||
if ($event)
|
||||
$event.preventDefault()
|
||||
$event.stopPropagation()
|
||||
|
||||
return if (elem.attr('disabled'))
|
||||
input = angular.element('<input name="project[state]" type="hidden" value="published">')
|
||||
form = angular.element('form')
|
||||
form.append(input)
|
||||
form.triggerHandler('submit')
|
||||
form[0].submit()
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Application.Directives.directive "disableAnimation", ($animate) ->
|
||||
restrict: "A"
|
||||
link: (scope, elem, attrs) ->
|
||||
attrs.$observe "disableAnimation", (value) ->
|
||||
$animate.enabled not value, elem
|
||||
|
10
app/assets/javascripts/directives/fab_user_avatar.coffee.erb
Normal file
@ -0,0 +1,10 @@
|
||||
Application.Directives.directive 'fabUserAvatar', [ ->
|
||||
{
|
||||
restrict: 'E'
|
||||
scope:
|
||||
userAvatar: "=ngModel"
|
||||
avatarClass: '@'
|
||||
templateUrl: '<%= asset_path "shared/_user_avatar.html" %>'
|
||||
}
|
||||
]
|
||||
|
119
app/assets/javascripts/filters/filters.coffee
Normal file
@ -0,0 +1,119 @@
|
||||
'use strict'
|
||||
|
||||
# filter for projects and trainings
|
||||
Application.Controllers.filter "machineFilter", [ ->
|
||||
(elements, selectedMachine) ->
|
||||
if !angular.isUndefined(elements) and !angular.isUndefined(selectedMachine) and elements? and selectedMachine?
|
||||
filteredElements = []
|
||||
angular.forEach elements, (element)->
|
||||
if element.machine_ids.indexOf(selectedMachine) != -1
|
||||
filteredElements.push(element)
|
||||
filteredElements
|
||||
else
|
||||
elements
|
||||
]
|
||||
|
||||
Application.Controllers.filter "projectMemberFilter", [ "Auth", (Auth)->
|
||||
(projects, selectedMember) ->
|
||||
if !angular.isUndefined(projects) and angular.isDefined(selectedMember) and projects? and selectedMember? and selectedMember != ""
|
||||
filteredProject = []
|
||||
# Mes projets
|
||||
if selectedMember == '0'
|
||||
angular.forEach projects, (project)->
|
||||
if project.author_id == Auth._currentUser.id
|
||||
filteredProject.push(project)
|
||||
# les projets auxquels je collabore
|
||||
else
|
||||
angular.forEach projects, (project)->
|
||||
if project.user_ids.indexOf(Auth._currentUser.id) != -1
|
||||
filteredProject.push(project)
|
||||
filteredProject
|
||||
else
|
||||
projects
|
||||
]
|
||||
|
||||
Application.Controllers.filter "themeFilter", [ ->
|
||||
(projects, selectedTheme) ->
|
||||
if !angular.isUndefined(projects) and !angular.isUndefined(selectedTheme) and projects? and selectedTheme?
|
||||
filteredProjects = []
|
||||
angular.forEach projects, (project)->
|
||||
if project.theme_ids.indexOf(selectedTheme) != -1
|
||||
filteredProjects.push(project)
|
||||
filteredProjects
|
||||
else
|
||||
projects
|
||||
]
|
||||
|
||||
Application.Controllers.filter "componentFilter", [ ->
|
||||
(projects, selectedComponent) ->
|
||||
if !angular.isUndefined(projects) and !angular.isUndefined(selectedComponent) and projects? and selectedComponent?
|
||||
filteredProjects = []
|
||||
angular.forEach projects, (project)->
|
||||
if project.component_ids.indexOf(selectedComponent) != -1
|
||||
filteredProjects.push(project)
|
||||
filteredProjects
|
||||
else
|
||||
projects
|
||||
]
|
||||
|
||||
Application.Controllers.filter "projectsByAuthor", [ ->
|
||||
(projects, authorId) ->
|
||||
if !angular.isUndefined(projects) and angular.isDefined(authorId) and projects? and authorId? and authorId != ""
|
||||
filteredProject = []
|
||||
angular.forEach projects, (project)->
|
||||
if project.author_id == authorId
|
||||
filteredProject.push(project)
|
||||
filteredProject
|
||||
else
|
||||
projects
|
||||
]
|
||||
|
||||
Application.Controllers.filter "projectsCollabored", [ ->
|
||||
(projects, memberId) ->
|
||||
if !angular.isUndefined(projects) and angular.isDefined(memberId) and projects? and memberId? and memberId != ""
|
||||
filteredProject = []
|
||||
angular.forEach projects, (project)->
|
||||
if project.user_ids.indexOf(memberId) != -1
|
||||
filteredProject.push(project)
|
||||
filteredProject
|
||||
else
|
||||
projects
|
||||
]
|
||||
|
||||
# depend on humanize.js lib in /vendor
|
||||
Application.Controllers.filter "humanize", [ ->
|
||||
(element, param) ->
|
||||
Humanize.truncate(element, param, null)
|
||||
]
|
||||
|
||||
Application.Controllers.filter "breakFilter", [ ->
|
||||
(text) ->
|
||||
if text != undefined
|
||||
text.replace(/\n/g, '<br />')
|
||||
]
|
||||
|
||||
Application.Controllers.filter "toTrusted", [ "$sce", ($sce) ->
|
||||
(text) ->
|
||||
$sce.trustAsHtml text
|
||||
]
|
||||
|
||||
|
||||
Application.Controllers.filter "eventsFilter", [ ->
|
||||
(elements, selectedScope) ->
|
||||
if !angular.isUndefined(elements) and !angular.isUndefined(selectedScope) and elements? and selectedScope? and selectedScope != ""
|
||||
filteredElements = []
|
||||
angular.forEach elements, (element)->
|
||||
element.start_at = element.availability.start_at if angular.isUndefined(element.start_at)
|
||||
switch selectedScope
|
||||
when "future"
|
||||
if new Date(element.start_at) > new Date
|
||||
filteredElements.push(element)
|
||||
when "passed"
|
||||
if new Date(element.start_at) <= new Date
|
||||
filteredElements.push(element)
|
||||
else
|
||||
return []
|
||||
filteredElements
|
||||
else
|
||||
elements
|
||||
]
|
222
app/assets/javascripts/router.coffee.erb
Normal file
@ -0,0 +1,222 @@
|
||||
angular.module('application.router', ['ui.router']).
|
||||
config ['$stateProvider', '$urlRouterProvider', '$locationProvider', ($stateProvider, $urlRouterProvider, $locationProvider) ->
|
||||
$locationProvider.hashPrefix('!')
|
||||
$urlRouterProvider.otherwise("/")
|
||||
|
||||
# abstract root parents states
|
||||
# these states controls the access rights to the various routes inherited from them
|
||||
$stateProvider
|
||||
.state 'app',
|
||||
abstract: true
|
||||
views:
|
||||
'header': { templateUrl: '<%= asset_path "shared/header.html" %>' }
|
||||
'leftnav':
|
||||
templateUrl: '<%= asset_path "shared/leftnav.html" %>'
|
||||
controller: 'mainNavController'
|
||||
'main':
|
||||
templateUrl: '<%= asset_path "home.html" %>'
|
||||
controller: 'homeController'
|
||||
.state 'app.public',
|
||||
abstract: true
|
||||
.state 'app.logged',
|
||||
abstract: true
|
||||
data:
|
||||
authorizedRoles: ['member', 'admin']
|
||||
resolve:
|
||||
currentUser: ['Auth', (Auth)->
|
||||
Auth.currentUser()
|
||||
]
|
||||
onEnter: ["currentUser", "$rootScope", (currentUser, $rootScope)->
|
||||
$rootScope.currentUser = currentUser
|
||||
]
|
||||
.state 'app.admin',
|
||||
abstract: true
|
||||
data:
|
||||
authorizedRoles: ['admin']
|
||||
resolve:
|
||||
currentUser: ['Auth', (Auth)->
|
||||
Auth.currentUser()
|
||||
]
|
||||
onEnter: ["currentUser", "$rootScope", (currentUser, $rootScope)->
|
||||
$rootScope.currentUser = currentUser
|
||||
]
|
||||
|
||||
|
||||
|
||||
# main pages
|
||||
.state 'app.public.about',
|
||||
url: '/about'
|
||||
views:
|
||||
'content@': { templateUrl: '<%= asset_path "shared/about.html" %>' }
|
||||
.state 'app.public.home',
|
||||
url: '/?reset_password_token'
|
||||
views:
|
||||
'main':
|
||||
templateUrl: '<%= asset_path "home.html" %>'
|
||||
controller: 'homeController'
|
||||
|
||||
|
||||
# dashboard
|
||||
.state 'app.logged.dashboard_profile',
|
||||
url: '/dashboard/profile'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "dashboard/profile.html" %>'
|
||||
controller: 'editProfileController'
|
||||
.state 'app.logged.dashboard_projects',
|
||||
url: '/dashboard/projects'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "dashboard/projects.html" %>'
|
||||
controller: 'dashboardProjectsController'
|
||||
|
||||
|
||||
# members
|
||||
.state 'app.logged.members_show',
|
||||
url: '/members/:id'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "members/show.html" %>'
|
||||
controller: 'showProfileController'
|
||||
.state 'app.logged.members',
|
||||
url: '/members'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "members/index.html" %>'
|
||||
controller: 'membersController'
|
||||
|
||||
|
||||
# projects
|
||||
.state 'app.public.projects_list',
|
||||
url: '/projects'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "projects/index.html" %>'
|
||||
controller: 'projectsController'
|
||||
.state 'app.public.projects_show',
|
||||
url: '/projects/:id'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "projects/show.html" %>'
|
||||
controller: 'showProjectController'
|
||||
.state 'app.logged.projects_new',
|
||||
url: '/projects/new'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "projects/new.html" %>'
|
||||
controller: 'newProjectController'
|
||||
.state 'app.logged.projects_edit',
|
||||
url: '/projects/:id/edit'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "projects/edit.html" %>'
|
||||
controller: 'editProjectController'
|
||||
|
||||
|
||||
|
||||
# machines
|
||||
.state 'app.public.machines_list',
|
||||
url: '/machines'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "machines/index.html" %>'
|
||||
controller: 'machinesController'
|
||||
.state 'app.public.machines_show',
|
||||
url: '/machines/:id'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "machines/show.html" %>'
|
||||
controller: 'showMachineController'
|
||||
.state 'app.admin.machines_new',
|
||||
url: '/machines/new'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "machines/new.html" %>'
|
||||
controller: 'newMachineController'
|
||||
.state 'app.admin.machines_edit',
|
||||
url: '/machines/:id/edit'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "machines/edit.html" %>'
|
||||
controller: 'editMachineController'
|
||||
|
||||
|
||||
# notifications
|
||||
.state 'app.logged.notifications',
|
||||
url: '/notifications'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "notifications/index.html" %>'
|
||||
controller: 'notificationsController'
|
||||
|
||||
|
||||
# events
|
||||
.state 'app.public.events_list',
|
||||
url: '/events'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "events/index.html" %>'
|
||||
controller: 'eventsController'
|
||||
.state 'app.public.events_show',
|
||||
url: '/events/:id'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "events/show.html" %>'
|
||||
controller: 'showEventController'
|
||||
|
||||
|
||||
# --- namespace /admin/... ---
|
||||
|
||||
|
||||
# project's elements
|
||||
.state 'app.admin.project_elements',
|
||||
url: '/admin/project_elements'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "admin/project_elements/index.html" %>'
|
||||
controller: 'projectElementsController'
|
||||
|
||||
|
||||
# events
|
||||
.state 'app.admin.events',
|
||||
url: '/admin/events'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "admin/events/index.html" %>'
|
||||
controller: 'adminEventsController'
|
||||
.state 'app.admin.events_new',
|
||||
url: '/admin/events/new'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "events/new.html" %>'
|
||||
controller: 'newEventController'
|
||||
.state 'app.admin.events_edit',
|
||||
url: '/admin/events/:id/edit'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "events/edit.html" %>'
|
||||
controller: 'editEventController'
|
||||
|
||||
|
||||
# members
|
||||
.state 'app.admin.members',
|
||||
url: '/admin/members'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "admin/members/index.html" %>'
|
||||
controller: 'membersController'
|
||||
.state 'app.admin.members_new',
|
||||
url: '/admin/members/new'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "admin/members/new.html" %>'
|
||||
controller: 'newMemberController'
|
||||
.state 'app.admin.members_edit',
|
||||
url: '/admin/members/:id/edit'
|
||||
views:
|
||||
'main@':
|
||||
templateUrl: '<%= asset_path "admin/members/edit.html" %>'
|
||||
controller: 'editMemberController'
|
||||
|
||||
|
||||
]
|
12
app/assets/javascripts/services/auth.coffee
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'AuthService', ["Session", (Session) ->
|
||||
isAuthenticated: ->
|
||||
Session.currentUser? and Session.currentUser.id?
|
||||
|
||||
isAuthorized: (authorizedRoles) ->
|
||||
if !angular.isArray(authorizedRoles)
|
||||
authorizedRoles = [authorizedRoles]
|
||||
|
||||
@isAuthenticated() and authorizedRoles.indexOf(Session.currentUser.role) != -1
|
||||
]
|
8
app/assets/javascripts/services/category.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Category', ["$resource", ($resource)->
|
||||
$resource "/api/categories/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
]
|
8
app/assets/javascripts/services/component.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Component', ["$resource", ($resource)->
|
||||
$resource "/api/components/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
]
|
14
app/assets/javascripts/services/csrf.coffee
Normal file
@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.service 'CSRF', ['$cookies',
|
||||
($cookies)->
|
||||
return {
|
||||
setMetaTags: ->
|
||||
if angular.element('meta[name="csrf-param"]').length == 0
|
||||
angular.element('head').append("<meta name=\"csrf-param\" content=\"authenticity_token\">")
|
||||
angular.element('head').append("<meta name=\"csrf-token\" content=\"#{$cookies['XSRF-TOKEN']}\">")
|
||||
else
|
||||
angular.element('meta[name="csrf-token"]').replaceWith("<meta name=\"csrf-token\" content=\"#{$cookies['XSRF-TOKEN']}\">")
|
||||
return
|
||||
}
|
||||
]
|
27
app/assets/javascripts/services/dialogs.coffee.erb
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'dialogs', ["$modal", ($modal) ->
|
||||
confirm: (options, success, error)->
|
||||
defaultOpts =
|
||||
templateUrl: '<%= asset_path "shared/confirm_modal.html" %>'
|
||||
size: 'sm'
|
||||
resolve:
|
||||
object: ->
|
||||
title: 'Titre de confirmation'
|
||||
msg: 'Message de confiramtion'
|
||||
controller: ['$scope', '$modalInstance', '$state', 'object', ($scope, $modalInstance, $state, object) ->
|
||||
$scope.object = object
|
||||
$scope.ok = ->
|
||||
$modalInstance.close()
|
||||
$scope.cancel = ->
|
||||
$modalInstance.dismiss('cancel')
|
||||
]
|
||||
angular.extend(defaultOpts, options) if angular.isObject options
|
||||
$modal.open defaultOpts
|
||||
.result['finally'](null).then ->
|
||||
if angular.isFunction(success)
|
||||
success()
|
||||
, ->
|
||||
if angular.isFunction(error)
|
||||
error()
|
||||
]
|
13
app/assets/javascripts/services/event.coffee
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Event', ["$resource", ($resource)->
|
||||
$resource "/api/events/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
upcoming:
|
||||
method: 'GET'
|
||||
url: '/api/events/upcoming/:limit'
|
||||
params: {limit: "@limit"}
|
||||
isArray: true
|
||||
]
|
6
app/assets/javascripts/services/group.coffee
Normal file
@ -0,0 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Group', ["$resource", ($resource)->
|
||||
$resource "/api/groups/:id",
|
||||
{id: "@id"}
|
||||
]
|
8
app/assets/javascripts/services/licence.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Licence', ["$resource", ($resource)->
|
||||
$resource "/api/licences/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
]
|
8
app/assets/javascripts/services/machine.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Machine', ["$resource", ($resource)->
|
||||
$resource "/api/machines/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
]
|
11
app/assets/javascripts/services/member.coffee
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Member', ["$resource", ($resource)->
|
||||
$resource "/api/members/:id",
|
||||
{id: "@id"},
|
||||
lastSubscribed:
|
||||
method: 'GET'
|
||||
url: '/api/last_subscribed/:limit'
|
||||
params: {limit: "@limit"}
|
||||
isArray: true
|
||||
]
|
8
app/assets/javascripts/services/notification.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Notification', ["$resource", ($resource)->
|
||||
$resource "/api/notifications/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
]
|
10
app/assets/javascripts/services/project.coffee
Normal file
@ -0,0 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Project', ["$resource", ($resource)->
|
||||
$resource "/api/projects/:id",
|
||||
{id: "@id"},
|
||||
lastPublished:
|
||||
method: 'GET'
|
||||
url: '/api/projects/last_published'
|
||||
isArray: true
|
||||
]
|
11
app/assets/javascripts/services/session.coffee
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.service 'Session', [ ->
|
||||
@create = (user)->
|
||||
@currentUser = user
|
||||
|
||||
@destroy = ->
|
||||
@currentUser = null
|
||||
|
||||
return @
|
||||
]
|
8
app/assets/javascripts/services/theme.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Theme', ["$resource", ($resource)->
|
||||
$resource "/api/themes/:id",
|
||||
{id: "@id"},
|
||||
update:
|
||||
method: 'PUT'
|
||||
]
|
5
app/assets/javascripts/services/twitter.coffee
Normal file
@ -0,0 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
Application.Services.factory 'Twitter', ["$resource", ($resource)->
|
||||
$resource "/api/feeds/twitter_timelines"
|
||||
]
|
169
app/assets/stylesheets/app.base.scss
Normal file
@ -0,0 +1,169 @@
|
||||
// reset
|
||||
html {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.h1, .h2, .h3, .h4, .h5, .h6{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1, .page-title {
|
||||
line-height: rem-calc(24);
|
||||
text-transform: uppercase;
|
||||
font-weight: 900;
|
||||
}
|
||||
h2 {
|
||||
color: $red;
|
||||
line-height: rem-calc(24);
|
||||
font-weight: 900;
|
||||
}
|
||||
h3 { font-weight: 600; }
|
||||
h4 { font-weight: bold; }
|
||||
h5 {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
line-height: rem-calc(18);
|
||||
color: $red;
|
||||
font-size: rem-calc(16);
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0px;
|
||||
content: '';
|
||||
width: 35%;
|
||||
height: 1px;
|
||||
background-color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
// Links
|
||||
// -------------------------
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: $link-hover-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
label{ font-weight: 600; }
|
||||
small, .small{font-size: $font-size-sm;}
|
||||
|
||||
pre { padding: 0; }
|
||||
|
||||
p {
|
||||
font-size: rem-calc($font-size-base);
|
||||
line-height: rem-calc(24);
|
||||
|
||||
&.intro, .intro {
|
||||
font-family: $font-proxima-condensed;
|
||||
font-size: rem-calc(16);
|
||||
line-height: rem-calc(24);
|
||||
margin: 1.038em 0px 30px 0px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
dt {
|
||||
color: $black-light;
|
||||
}
|
||||
dd {
|
||||
color: $black-light;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// -------------------------
|
||||
|
||||
.col-0{clear:left;}
|
||||
|
||||
.row.no-gutter{
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.no-gutter > [class*="col"]{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* centered columns styles */
|
||||
.row-centered {
|
||||
text-align:center;
|
||||
}
|
||||
.col-centered {
|
||||
display:inline-block;
|
||||
float:none;
|
||||
/* reset the text-align */
|
||||
text-align:left;
|
||||
/* inline-block space fix */
|
||||
margin-right:-4px;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-input-placeholder { /* WebKit browsers */
|
||||
color: black;
|
||||
}
|
||||
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
|
||||
color: black;
|
||||
opacity: 1;
|
||||
}
|
||||
::-moz-placeholder { /* Mozilla Firefox 19+ */
|
||||
color: black;
|
||||
opacity: 1;
|
||||
}
|
||||
:-ms-input-placeholder { /* Internet Explorer 10+ */
|
||||
color: black;
|
||||
}
|
||||
|
||||
// .ng-hide-remove-active {
|
||||
// -webkit-transition:0.5s linear all;
|
||||
// transition:0.5s linear all;
|
||||
// }
|
||||
|
||||
[ui-view].ng-enter, [ui-view].ng-leave {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@include transition(all .7s ease-in-out);
|
||||
}
|
||||
|
||||
[ui-view].ng-enter {
|
||||
opacity: 0;
|
||||
// -webkit-transform:scale3d(0.5, 0.5, 0.5);
|
||||
// -moz-transform:scale3d(0.5, 0.5, 0.5);
|
||||
// transform:scale3d(0.5, 0.5, 0.5);
|
||||
}
|
||||
|
||||
[ui-view].ng-enter-active {
|
||||
opacity: 1;
|
||||
// -webkit-transform:scale3d(1, 1, 1);
|
||||
// -moz-transform:scale3d(1, 1, 1);
|
||||
// transform:scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
[ui-view].ng-leave {
|
||||
opacity: 1;
|
||||
/*padding-left: 0px;*/
|
||||
// -webkit-transform:translate3d(0, 0, 0);
|
||||
// -moz-transform:translate3d(0, 0, 0);
|
||||
// transform:translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
[ui-view].ng-leave-active {
|
||||
opacity: 0;
|
||||
/*padding-left: 100px;*/
|
||||
// -webkit-transform:translate3d(100px, 0, 0);
|
||||
// -moz-transform:translate3d(100px, 0, 0);
|
||||
// transform:translate3d(100px, 0, 0);
|
||||
}
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
44
app/assets/stylesheets/app.buttons.scss
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open > .btn-default.dropdown-toggle {
|
||||
|
||||
background-color: #f2f2f2;
|
||||
|
||||
}
|
||||
|
||||
.btn{
|
||||
> i{
|
||||
&.pull-left,
|
||||
&.pull-right{
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-warning-full {
|
||||
outline: 0;
|
||||
text-transform: uppercase;
|
||||
border: 3px solid $yellow;
|
||||
background-color: $yellow;
|
||||
&:hover {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn-block {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.btn-group-vertical > .btn:first-child:not(:last-child){
|
||||
border-top-right-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
.btn-group-vertical > .btn:last-child:not(:first-child){
|
||||
border-bottom-left-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
.btn-inactive{
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
36
app/assets/stylesheets/app.colors.scss
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
.bg-light { background-color: $brand-light; }
|
||||
.bg-red { background-color: $red; color: white; }
|
||||
.bg-red-dark { background-color: $red-dark; }
|
||||
.bg-yellow { background-color: $yellow !important; }
|
||||
.bg-machine { background-color: $beige; }
|
||||
.bg-formation { background-color: $violet; }
|
||||
.bg-atelier { background-color: $blue; }
|
||||
.bg-stage { background-color: $violet; }
|
||||
.bg-success { background-color: $brand-success; }
|
||||
.bg-info { background-color: $brand-info; }
|
||||
|
||||
.bg-black-light { background-color: #424242 !important; }
|
||||
|
||||
.bg-white {
|
||||
background-color: #fff;
|
||||
color: $text-color;
|
||||
a {
|
||||
color: $link-color;
|
||||
&:hover{
|
||||
color: darken($link-color, 10%);
|
||||
}
|
||||
}
|
||||
.text-muted{color: $text-muted !important;}
|
||||
}
|
||||
.bg-white-only{background-color:#fff;}
|
||||
.bg-empty{background-color: transparent;}
|
||||
|
||||
.text-black{ color: #000 !important; };
|
||||
.text-black-light { color: #424242 !important; }
|
||||
.text-gray { color: #5a5a5a !important; }
|
||||
.text-white { color: #fff !important; }
|
||||
.text-yellow { color: $yellow !important; }
|
||||
.text-blue { color: $blue; }
|
||||
.text-muted { color: $text-muted; }
|
||||
.text-danger, .red { color: $red !important; }
|
438
app/assets/stylesheets/app.components.scss
Normal file
@ -0,0 +1,438 @@
|
||||
.widget {
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
line-height: rem-calc(18);
|
||||
font-size: rem-calc(14);
|
||||
font-weight: 600;
|
||||
color: black;
|
||||
}
|
||||
h1 {
|
||||
font-size: rem-calc(16); text-transform: uppercase;
|
||||
}
|
||||
h2 { font-weight: bold; }
|
||||
h3 { color: $red; }
|
||||
h4 {
|
||||
font-size: rem-calc(12);
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
|
||||
p { font-size: rem-calc(14); margin-top: 15px; }
|
||||
|
||||
a {
|
||||
color: $black-light;
|
||||
&:hover { color: $red; }
|
||||
}
|
||||
.fa {
|
||||
// color: $red;
|
||||
}
|
||||
|
||||
.widget-content {
|
||||
font-size: rem-calc(14);
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
&.no-b {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//modal
|
||||
.modal-dialog { top: 90px; }
|
||||
|
||||
.modal-header {
|
||||
.modal-logo {
|
||||
position: absolute;
|
||||
top: -70px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
margin: 25px 0 20px 0;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.box-thumb {
|
||||
opacity: 0.9;
|
||||
&:hover { opacity: 1;}
|
||||
&:hover .box-footer { opacity: 1; }
|
||||
position: relative;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px;
|
||||
cursor: pointer;
|
||||
// todo
|
||||
overflow: hidden;
|
||||
height: 280px;
|
||||
img {
|
||||
opacity: 0.9;
|
||||
|
||||
}
|
||||
.project-caption {
|
||||
text-shadow: rgba(29, 29, 29, 0.5) 0 -1px, rgba(29, 29, 29, 0.5) -1px 0,
|
||||
rgba(29, 29, 29, 0.5) 1px 0, rgba(29, 29, 29, 0.5) 0 1px;
|
||||
}
|
||||
.box-content {
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
h1 {
|
||||
padding: 0 20px;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-size: rem-calc(24);
|
||||
font-weight: 900;
|
||||
line-height: rem-calc(30);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.box-footer {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.article {
|
||||
max-width: $screen-md-min;
|
||||
margin: 0 auto;
|
||||
|
||||
h2, h3, h4, h5 { margin: 1.8em 0 1em 0; }
|
||||
|
||||
h2, h3 {
|
||||
color: $red;
|
||||
font-size: rem-calc(18);
|
||||
font-weight: bold;
|
||||
}
|
||||
h2 { text-transform: uppercase; }
|
||||
h4 {
|
||||
font-weight: 600;
|
||||
font-size: rem-calc(16);
|
||||
color: #686868;
|
||||
}
|
||||
|
||||
.article-thumbnail {
|
||||
max-height: 400px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.label-staging {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: -13px;
|
||||
}
|
||||
|
||||
.notification-open {
|
||||
a {
|
||||
position: relative;
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: 18px; right: 18px;
|
||||
padding: 3px 6px 1px 6px;
|
||||
}
|
||||
}
|
||||
.fa { color: black; font-size: rem-calc(24); }
|
||||
}
|
||||
|
||||
|
||||
.panel {
|
||||
margin-bottom: 30px;
|
||||
.panel-heading {
|
||||
&.small {
|
||||
padding: 15px 15px;
|
||||
}
|
||||
&.picture {
|
||||
height: 250px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
@include transition(opacity .5s ease);
|
||||
cursor: pointer;
|
||||
padding:0;
|
||||
img { @include border-radius(6px 6px 0 0); }
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.align {
|
||||
position: relative;
|
||||
top: -7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pricing-panel {
|
||||
border: 1px solid $border-color;
|
||||
|
||||
&:first-child {
|
||||
border-right: none;
|
||||
@include border-radius(3px 0 0 3px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-radius(0 3px 3px 0);
|
||||
}
|
||||
.title {
|
||||
margin: 10px 0;
|
||||
font-size: rem-calc(16);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.content {
|
||||
padding: 15px 0;
|
||||
background-color: $bg-gray;
|
||||
.wrap {
|
||||
width: 100px; height: 100px;
|
||||
display: inline-block;
|
||||
background: white;
|
||||
@include border-radius(50%);
|
||||
border: 3px solid $yellow;
|
||||
}
|
||||
.price {
|
||||
position: relative;
|
||||
top: 5px; left: 5px;
|
||||
height: 84px; width: 84px;
|
||||
background-color: black;
|
||||
@include border-radius(50%);
|
||||
|
||||
.amount {
|
||||
padding-top: 16px;
|
||||
font-weight: bold;
|
||||
font-size: rem-calc(26);
|
||||
color: white;
|
||||
}
|
||||
.period {
|
||||
position: relative;
|
||||
top: -14px;
|
||||
font-size: rem-calc(14);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cta-button {
|
||||
margin: 20px 0;
|
||||
.btn {
|
||||
outline: 0;
|
||||
font-weight: 600;
|
||||
font-size: rem-calc(16);
|
||||
background-color: white;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
&:hover { background-color: $yellow; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.well {
|
||||
&.well-warning {
|
||||
border-color: #ffdc4e;
|
||||
background-color: #ffdc4e;
|
||||
@include border-radius(3px);
|
||||
padding: 5px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.read {
|
||||
opacity: 0.7;
|
||||
background: #F2F2F2;
|
||||
}
|
||||
|
||||
.badge {
|
||||
&.inverse {
|
||||
color: black;
|
||||
background-color: $yellow;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar{
|
||||
position: relative;
|
||||
display: block;
|
||||
border-radius: 500px;
|
||||
white-space: nowrap;
|
||||
&.avatar-block {
|
||||
white-space: inherit;
|
||||
height: 76px;
|
||||
.user-name {
|
||||
display: block;
|
||||
font-size: rem-calc(14);
|
||||
line-height: rem-calc(14);
|
||||
}
|
||||
}
|
||||
img{
|
||||
border-radius: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
i{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-radius: 100%;
|
||||
&.md{
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin: 1px;
|
||||
}
|
||||
&.right{
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
&.bottom{
|
||||
left: auto;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
&.on{
|
||||
background-color: $brand-success;
|
||||
}
|
||||
&.off{
|
||||
background-color: $text-muted;
|
||||
}
|
||||
&.busy{
|
||||
background-color: $brand-danger;
|
||||
}
|
||||
&.away{
|
||||
background-color: $brand-warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.block-link {
|
||||
cursor: pointer;
|
||||
&:hover { background-color: $yellow; }
|
||||
}
|
||||
|
||||
.form-control.form-control-ui-select .select2-choices .select2-search-choice {
|
||||
font-size: 85% !important;
|
||||
}
|
||||
|
||||
.about-link {
|
||||
.label {
|
||||
font-size: rem-calc(26);
|
||||
padding: 0px 10px 0px 11px;
|
||||
vertical-align: bottom;
|
||||
&.label-icon { font-size: rem-calc(22); padding: 4px 9px 1px 10px; }
|
||||
}
|
||||
}
|
||||
|
||||
.about-fablab {
|
||||
position: relative;
|
||||
z-index: 101;
|
||||
height: 93%;
|
||||
background-color: white;
|
||||
@include transition(.5s linear all);
|
||||
opacity:1;
|
||||
|
||||
.about-picture {
|
||||
padding: 70px 0;
|
||||
height: 326px;
|
||||
background: white asset-url("about-fablab.jpg") no-repeat;
|
||||
background-size: cover;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.about-title {
|
||||
margin: 0;
|
||||
font-size: rem-calc(50);
|
||||
line-height: rem-calc(48);
|
||||
color: #fff;
|
||||
font-weight: 900; //black
|
||||
}
|
||||
|
||||
.about-title-aside {
|
||||
margin-top: 0;
|
||||
font-size: rem-calc(18);
|
||||
}
|
||||
p {
|
||||
font-size: rem-calc(18);
|
||||
line-height: rem-calc(30);
|
||||
color: $black-light;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
&.ng-hide {
|
||||
opacity: 0;
|
||||
@include transition(.5s linear all);
|
||||
}
|
||||
|
||||
&.ng-hide-add,
|
||||
&.ng-hide-remove {
|
||||
display:block!important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
.event {
|
||||
transition: all 0.07s linear;
|
||||
}
|
||||
|
||||
.event:hover {
|
||||
background-color: #cb1117;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.event:hover * {
|
||||
color: #eee;
|
||||
border-color: #eee;
|
||||
}
|
||||
|
||||
.box-h-m {
|
||||
height: 150px;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.half-w {
|
||||
width: 50%;
|
||||
}
|
||||
.b-light-dark {
|
||||
border-color: #d0d0d0;
|
||||
}
|
||||
|
||||
.p-sm {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.crop-130 {
|
||||
height: 130px;
|
||||
width: 130px;
|
||||
max-width: 130px;
|
||||
max-height: 130px;
|
||||
overflow: hidden;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.crop-130 img {
|
||||
height: 130px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1280px) and (min-width: 770px) {
|
||||
.crop-130 {
|
||||
height: 90px;
|
||||
width: 90px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
63
app/assets/stylesheets/app.functions.scss
Normal file
@ -0,0 +1,63 @@
|
||||
// This is the default html and body font-size for the base rem value.
|
||||
$rem-base: 10px !default;
|
||||
|
||||
|
||||
// IMPORT ONCE
|
||||
// We use this to prevent styles from being loaded multiple times for compenents that rely on other components.
|
||||
$modules: () !default;
|
||||
@mixin exports($name) {
|
||||
@if (index($modules, $name) == false) {
|
||||
$modules: append($modules, $name);
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @functions
|
||||
//
|
||||
|
||||
|
||||
// STRIP UNIT
|
||||
// It strips the unit of measure and returns it
|
||||
@function strip-unit($num) {
|
||||
@return $num / ($num * 0 + 1);
|
||||
}
|
||||
|
||||
// CONVERT TO REM
|
||||
@function convert-to-rem($value, $base-value: $rem-base) {
|
||||
$value: strip-unit($value) / strip-unit($base-value) * 1rem;
|
||||
@if ($value == 0rem) { $value: 0; } // Turn 0rem into 0
|
||||
@return $value;
|
||||
}
|
||||
|
||||
|
||||
// REM CALC
|
||||
|
||||
// New Syntax, allows to optionally calculate on a different base value to counter compounding effect of rem's.
|
||||
// Call with 1, 2, 3 or 4 parameters, 'px' is not required but supported:
|
||||
//
|
||||
// rem-calc(10 20 30px 40);
|
||||
//
|
||||
// Space delimited, if you want to delimit using comma's, wrap it in another pair of brackets
|
||||
//
|
||||
// rem-calc((10, 20, 30, 40px));
|
||||
//
|
||||
// Optionally call with a different base (eg: 8px) to calculate rem.
|
||||
//
|
||||
// rem-calc(16px 32px 48px, 8px);
|
||||
//
|
||||
// If you require to comma separate your list
|
||||
//
|
||||
// rem-calc((16px, 32px, 48), 8px);
|
||||
|
||||
@function rem-calc($values, $base-value: $rem-base) {
|
||||
$max: length($values);
|
||||
|
||||
@if $max == 1 { @return convert-to-rem(nth($values, 1), $base-value); }
|
||||
|
||||
$remValues: ();
|
||||
@for $i from 1 through $max {
|
||||
$remValues: append($remValues, convert-to-rem(nth($values, $i), $base-value));
|
||||
}
|
||||
@return $remValues;
|
||||
}
|
344
app/assets/stylesheets/app.layout.scss
Normal file
@ -0,0 +1,344 @@
|
||||
/*layout*/
|
||||
.header,
|
||||
.footer{
|
||||
min-height: 50px;
|
||||
padding: 0 15px;
|
||||
> p{
|
||||
margin-top: 15px;
|
||||
display: inline-block;
|
||||
}
|
||||
> .btn,
|
||||
> .btn-group,
|
||||
> .btn-toolbar,
|
||||
{
|
||||
margin-top: 14px;
|
||||
}
|
||||
> .btn-lg{
|
||||
margin-top: 0;
|
||||
}
|
||||
.nav-tabs {
|
||||
border: none;
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
> li {
|
||||
a{
|
||||
border: none !important;
|
||||
border-radius: 0;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
line-height: 20px;
|
||||
&:hover,
|
||||
&:focus
|
||||
{
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
&.active a{
|
||||
color: $text-color;
|
||||
&,
|
||||
&:hover{
|
||||
background-color: $body-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.nav-white{
|
||||
> li.active a{
|
||||
&,
|
||||
&:hover{
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.navbar{
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
margin-bottom: 0;
|
||||
padding:0;
|
||||
position: relative !important;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: $header-bg;
|
||||
@include box-shadow(0 1px 0px #cfcfcf);
|
||||
}
|
||||
|
||||
|
||||
.heading {
|
||||
|
||||
.heading-btn {
|
||||
a {
|
||||
width: 100%;
|
||||
padding: 35px 40%;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
color: black;
|
||||
&:hover {
|
||||
background-color: $yellow;
|
||||
}
|
||||
i:before { content: "\f177"; }
|
||||
}
|
||||
}
|
||||
.heading-title {
|
||||
overflow: hidden;
|
||||
height: 94px;
|
||||
h1 {
|
||||
margin: 0 0 0 15px;
|
||||
padding: 36px 15px;
|
||||
}
|
||||
}
|
||||
.heading-actions {
|
||||
height: 94px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
body.container{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.aside-md{
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
body.container{
|
||||
@include box-shadow(0 3px 60px rgba(0,0,0,0.3));
|
||||
border-left: 1px solid darken($border-color, 10%);
|
||||
border-right: 1px solid darken($border-color, 10%);
|
||||
}
|
||||
.app{
|
||||
&,
|
||||
body{
|
||||
height:100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hbox{
|
||||
&.stretch{
|
||||
height:100%;
|
||||
}
|
||||
}
|
||||
.vbox{
|
||||
> section,
|
||||
> footer{
|
||||
position: absolute;
|
||||
}
|
||||
&.flex{
|
||||
> section{
|
||||
> section{
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hbox{
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
> aside,
|
||||
> section{
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
height: 100%;
|
||||
float: none;
|
||||
&.show,
|
||||
&.hidden-sm {
|
||||
display: table-cell !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vbox{
|
||||
display: table;
|
||||
border-spacing:0;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
> section,
|
||||
> footer {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
> header{
|
||||
~ section {
|
||||
top: 50px;
|
||||
}
|
||||
&.header-md{
|
||||
~ section {
|
||||
top: $header-md-height+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
> section{
|
||||
&.w-f{
|
||||
bottom: 50px;
|
||||
}
|
||||
}
|
||||
> footer {
|
||||
top: auto;
|
||||
z-index: 100;
|
||||
~ section {
|
||||
bottom: 50px;
|
||||
}
|
||||
}
|
||||
&.flex{
|
||||
> header,
|
||||
> section,
|
||||
> footer {
|
||||
position: inherit;
|
||||
}
|
||||
> section{
|
||||
display: table-row;
|
||||
height: 100%;
|
||||
> section {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling:touch;
|
||||
.ie & {
|
||||
display: table-cell;
|
||||
}
|
||||
> section {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aside-xs{
|
||||
width: 60px;
|
||||
}
|
||||
.aside-sm{
|
||||
width: 150px;
|
||||
}
|
||||
.aside{
|
||||
width: 200px;
|
||||
}
|
||||
.aside-md{
|
||||
width: 250px;
|
||||
}
|
||||
.aside-lg{
|
||||
width: 300px;
|
||||
}
|
||||
.aside-xl{
|
||||
width: 360px;
|
||||
}
|
||||
.aside-xxl{
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
.header-md{
|
||||
height: $header-md-height;
|
||||
.navbar-form{
|
||||
margin-top: floor( ($header-md-height - 30)/2 );
|
||||
margin-bottom: floor( ($header-md-height - 30)/2 );
|
||||
}
|
||||
}
|
||||
|
||||
.scrollable{
|
||||
-webkit-overflow-scrolling:touch;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color:rgba(50,50,50,0.25);
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover{
|
||||
background-color:rgba(50,50,50,0.5);
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color:rgba(50,50,50,0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.scrollable{
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.no-touch {
|
||||
.scrollable.hover {
|
||||
overflow-y: hidden;
|
||||
&:hover
|
||||
{
|
||||
overflow: visible;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
::-webkit-scrollbar-button {
|
||||
width: 10px;
|
||||
height: 6px;
|
||||
background-color:rgba(50,50,50,0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.slimScrollBar{
|
||||
border-radius: 5px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 10px;
|
||||
background-clip: padding-box !important;
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
html, body, .hbox, .vbox{
|
||||
height: auto;
|
||||
}
|
||||
.vbox{
|
||||
> section,
|
||||
> footer{
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.datepicker-container {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.datepicker-dropdown {
|
||||
border: 1px solid #c7c5c5;
|
||||
border-radius: 0.3em;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
position: absolute;
|
||||
right:0;
|
||||
left: 0;
|
||||
background-color: #ffffff;
|
||||
|
||||
li {
|
||||
padding-left: 0.8em;
|
||||
padding-right: 0.8em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 2.4em;
|
||||
vertical-align: middle;
|
||||
|
||||
div.input-group {
|
||||
float:right;
|
||||
|
||||
ul.dropdown-menu {
|
||||
left: -5em !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
456
app/assets/stylesheets/app.nav.scss
Normal file
@ -0,0 +1,456 @@
|
||||
/*primary nav*/
|
||||
.navbar-header{
|
||||
position: relative;
|
||||
> .btn{
|
||||
position: absolute;
|
||||
font-size: 1.3em;
|
||||
padding: 9px 16px;
|
||||
line-height: 30px;
|
||||
right: 0;
|
||||
}
|
||||
.navbar-brand + .btn{
|
||||
right: 0;
|
||||
top:0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-brand{
|
||||
float: none;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
height: auto;
|
||||
line-height: 50px;
|
||||
display: inline-block;
|
||||
padding: 0 15px;
|
||||
&:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
img{
|
||||
max-height: 20px;
|
||||
margin-top: -4px;
|
||||
vertical-align: middle;
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-primary {
|
||||
li {
|
||||
> a > i{
|
||||
margin: floor(-($nav-primary-height - $line-height-computed)/2) -10px;
|
||||
line-height: $nav-primary-height;
|
||||
width: $nav-primary-height;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&:before{
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul.nav {
|
||||
> li {
|
||||
> a{
|
||||
padding: floor(($nav-primary-height - $line-height-computed)/2) 15px;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
@include transition(background-color .2s ease-in-out 0s);
|
||||
.no-borders & {
|
||||
border-width: 0 !important;
|
||||
}
|
||||
> .badge{
|
||||
font-size: 11px;
|
||||
padding: 2px 5px 2px 4px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
> .text-muted{
|
||||
margin: 0 3px;
|
||||
}
|
||||
&.active{
|
||||
.text{
|
||||
display: none;
|
||||
}
|
||||
.text-active{
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
li a{
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
&.active{
|
||||
> ul{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-black &{
|
||||
> ul.nav-main{
|
||||
> li{
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active{
|
||||
> a{
|
||||
background-color: $brand-success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.visible-nav-xs{display: none;}
|
||||
.nav-xs {
|
||||
width: $nav-xs-width;
|
||||
> .nav-container { width: $nav-xs-width; }
|
||||
.slimScrollDiv,
|
||||
.slim-scroll {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.slimScrollBar,
|
||||
.slimScrollRail {
|
||||
display: none !important;
|
||||
}
|
||||
.scrollable{
|
||||
overflow: visible;
|
||||
}
|
||||
.nav-primary{
|
||||
> ul {
|
||||
> li {
|
||||
> a {
|
||||
position: relative;
|
||||
padding: 0 !important;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
height: $nav-xs-height;
|
||||
overflow-y: hidden;
|
||||
border: none !important;
|
||||
span {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
height: $nav-xs-height;
|
||||
width: $nav-xs-width;
|
||||
&.pull-right{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
i{
|
||||
width: auto;
|
||||
float: none;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
line-height: $nav-xs-height;
|
||||
border: none !important;
|
||||
@include transition(margin-top 0.2s);
|
||||
b{
|
||||
left: 0 !important;
|
||||
}
|
||||
}
|
||||
.badge{
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 4px;
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active,
|
||||
{
|
||||
> a{
|
||||
i{
|
||||
margin-top: -$nav-xs-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ul{
|
||||
display: none !important;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0;
|
||||
z-index: 1050;
|
||||
width: 220px;
|
||||
-webkit-box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
li:hover,
|
||||
li:focus,
|
||||
li:active,
|
||||
{
|
||||
> ul{
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.nav-xs-right{
|
||||
.nav-primary > ul ul{
|
||||
left: auto;
|
||||
right: 100%;
|
||||
}
|
||||
}
|
||||
> .vbox > .header,
|
||||
> .vbox > .footer {
|
||||
padding:0 floor(($nav-xs-width - 30px)/2);
|
||||
}
|
||||
.hidden-nav-xs{
|
||||
display: none;
|
||||
}
|
||||
.visible-nav-xs{
|
||||
display: inherit;
|
||||
}
|
||||
.text-center-nav-xs{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-user{
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
.avatar{
|
||||
float: none !important;
|
||||
margin-right: 0;
|
||||
}
|
||||
.dropdown{
|
||||
> a{
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.navbar-header{
|
||||
float: none;
|
||||
}
|
||||
.navbar-brand{
|
||||
display: block;
|
||||
padding: 0;
|
||||
img{
|
||||
margin-right:0;
|
||||
}
|
||||
}
|
||||
.navbar{
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
.navbar-brand{
|
||||
.header-md &{
|
||||
line-height: $header-md-height;
|
||||
img{
|
||||
max-height: 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.navbar-nav{
|
||||
> li{
|
||||
> a{
|
||||
.header-md &{
|
||||
padding: floor(($header-md-height - $line-height-computed)/2 - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.navbar-fixed-top-xs{
|
||||
position: fixed !important;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1100;
|
||||
// + *{
|
||||
// padding-top: 50px !important;
|
||||
// }
|
||||
}
|
||||
.nav-bar-fixed-bottom{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 1100;
|
||||
}
|
||||
/* .off screen nav from left or right*/
|
||||
html, body{
|
||||
overflow-x: hidden;
|
||||
min-height: 100%;
|
||||
}
|
||||
.nav-primary{
|
||||
.dropdown-menu{
|
||||
position: relative;
|
||||
float:none;
|
||||
left: 0;
|
||||
margin-left: 0;
|
||||
padding: 0;
|
||||
a{
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
li:last-child{
|
||||
a{
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navbar-header{
|
||||
text-align: center;
|
||||
}
|
||||
.nav-user{
|
||||
margin: 0;
|
||||
padding: 15px;
|
||||
&.open{
|
||||
display: inherit !important;
|
||||
}
|
||||
.dropdown-menu{
|
||||
display: block;
|
||||
position: static;
|
||||
float: none;
|
||||
}
|
||||
.dropdown > a{
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.avatar{
|
||||
width: 160px !important;
|
||||
float: none !important;
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
padding: 5px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
top: 5px;
|
||||
border: 4px solid #fff;
|
||||
border-radius: 500px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-off-screen {
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0px;
|
||||
bottom: 0;
|
||||
width: $off-screen-nav-width;
|
||||
visibility: visible;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
.nav-primary{
|
||||
display: block !important;
|
||||
}
|
||||
.navbar-fixed-top-xs{
|
||||
width: $off-screen-nav-width;
|
||||
}
|
||||
&.push-right{
|
||||
.navbar-fixed-top-xs{
|
||||
left: 100% - $off-screen-nav-width;
|
||||
}
|
||||
}
|
||||
&.push-right{
|
||||
left: auto;
|
||||
right: 0;
|
||||
+ *{
|
||||
@include translate3d(-$off-screen-nav-width, 0px, 0px);
|
||||
}
|
||||
}
|
||||
+ *{
|
||||
background-color: $body-bg;
|
||||
@include transition-transform(0.2s ease-in-out);
|
||||
@include transition-delay(0s);
|
||||
@include translate3d(0px, 0px, 0px);
|
||||
@include backface-visibility(hidden);
|
||||
@include translate3d($off-screen-nav-width, 0px, 0px);
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
.nav-off-screen-block {
|
||||
display:block !important;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1950;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar + section{
|
||||
.nav-off-screen{
|
||||
top: 50px;
|
||||
+ *{
|
||||
top: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slimScrollDiv,
|
||||
.slim-scroll {
|
||||
overflow: visible !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.slimScrollBar,
|
||||
.slimScrollRail {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#nav {
|
||||
// border-right: 1px solid $red-dark;
|
||||
.nav {
|
||||
background-color: $red;
|
||||
> li {
|
||||
> a {
|
||||
padding: 13px 17px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
&:hover,
|
||||
&:focus, &.active {
|
||||
background-color: $red-light;
|
||||
color: white;
|
||||
}
|
||||
&.active {
|
||||
border-left: 3px solid #870003;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overrides bootstrap
|
||||
.nav-justified > li, .nav-tabs.nav-justified > li {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.user-profile-nav > a {
|
||||
display: inline-block !important;
|
||||
padding: 11px 44px !important;
|
||||
}
|
297
app/assets/stylesheets/app.plugins.scss
Normal file
@ -0,0 +1,297 @@
|
||||
// Redactor
|
||||
|
||||
.redactor_editor, .redactor_editor:focus {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
|
||||
// Growl
|
||||
.growl {
|
||||
top: 90px;
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UI Select
|
||||
|
||||
.form-control {
|
||||
&.form-control-ui-select {
|
||||
height: auto;
|
||||
.select2-choices {
|
||||
border: none;
|
||||
background: transparent;
|
||||
.select2-search-choice {
|
||||
@extend .label;
|
||||
padding-left: .9em;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.editable-input label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
Slider
|
||||
========================================================================== */
|
||||
|
||||
.carousel-caption {
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
text-align: left;
|
||||
background: rgba(29, 29, 29, 0.5);
|
||||
|
||||
.title {
|
||||
font-size: rem-calc(30);
|
||||
line-height: rem-calc(24);
|
||||
color: white;
|
||||
font-weight: 800;
|
||||
a {
|
||||
color: white;
|
||||
&:hover { color: $yellow; }
|
||||
}
|
||||
}
|
||||
.description {
|
||||
font-size: rem-calc(18);
|
||||
line-height: rem-calc(18);
|
||||
color: white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.carousel-control {
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
@include border-radius($border-radius-base);
|
||||
&:hover, &:focus {
|
||||
color: $yellow;
|
||||
}
|
||||
|
||||
.glyphicon-chevron-left {
|
||||
font-family: 'fontawesome' !important;
|
||||
&:before {
|
||||
content: "\f053" !important;
|
||||
}
|
||||
}
|
||||
|
||||
.glyphicon-chevron-right {
|
||||
font-family: 'fontawesome' !important;
|
||||
&:before {
|
||||
content: "\f054" !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.carousel-indicators {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.banner { }
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
&.slider-min {
|
||||
.slider-arrows {
|
||||
bottom: 40%;
|
||||
position: relative;
|
||||
}
|
||||
.slider-arrow {
|
||||
margin-bottom: 0;
|
||||
color: black;
|
||||
border: none;
|
||||
&.slider-arrow--left {
|
||||
left: -16px;
|
||||
}
|
||||
&.slider-arrow--right {
|
||||
right: -16px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 24px;
|
||||
&.icon-angle-left {
|
||||
width: 18px;
|
||||
}
|
||||
&:hover { color: $yellow; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
&.slider-actu {
|
||||
.slider-arrows {
|
||||
.slider-arrow--left { display: none; }
|
||||
.slider-arrow--right {
|
||||
bottom: 41px;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slides {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* Clear fix */
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
/**
|
||||
* Prevent blinking issue
|
||||
* Not tested. Experimental.
|
||||
*/
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.slide {
|
||||
height: 100%;
|
||||
float: left;
|
||||
clear: none;
|
||||
&.content_article {
|
||||
height: 620px;
|
||||
-webkit-background-size: cover;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.content_article-wrapper {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 215px;
|
||||
width: 45%;
|
||||
margin: 0 auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
.content_article-h {
|
||||
font-weight: 900;
|
||||
color: white;
|
||||
font-size: 50px;
|
||||
letter-spacing: 0.5px;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
line-height: 50px;
|
||||
}
|
||||
.content_article-c {
|
||||
color: $red;
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.content_article-p {
|
||||
// font-family: $font-proxima-condensed;
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
margin: 0;
|
||||
line-height: 30px;
|
||||
}
|
||||
.content_article-btns {
|
||||
margin-top: 40px;
|
||||
clear: both;
|
||||
.header_nav-item-a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.header_nav-item-a--btn {
|
||||
padding: 12px 20px;
|
||||
border-radius: 5px;
|
||||
border: 2px solid $yellow;
|
||||
background-color: transparent;
|
||||
text-transform: uppercase;
|
||||
font-family: "proxima-nova-condensed";
|
||||
font-weight: 600;
|
||||
&:hover {
|
||||
background-color: $yellow;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slider-arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
margin-bottom: -20px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
|
||||
width: 58px; height: 58px;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
&:hover { border-color: $yellow; color: $yellow; }
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 40px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
width: 12px;
|
||||
&.icon-angle-left {
|
||||
width: 18px;
|
||||
}
|
||||
&:hover { color: $yellow; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.home {
|
||||
.nav-branding {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-arrow--right {
|
||||
bottom: 50%;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.slider-arrow--left {
|
||||
bottom: 50%;
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.slider-nav {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
}
|
||||
|
||||
.slider-nav__item {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
float: left;
|
||||
clear: none;
|
||||
display: block;
|
||||
margin: 0 5px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.slider-nav__item:hover {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.slider-nav__item--current {
|
||||
background: #ccc;
|
||||
}
|
3
app/assets/stylesheets/app.printer.scss
Normal file
@ -0,0 +1,3 @@
|
||||
/*
|
||||
* Require here your print media stylesheets
|
||||
*/
|
38
app/assets/stylesheets/app.responsive.scss
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
@media screen and (max-width: $screen-sm-min) {
|
||||
.modal-dialog { top: 0; }
|
||||
.modal-header {
|
||||
.modal-logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.about-fablab {
|
||||
.about-title {
|
||||
font-size: rem-calc(30);
|
||||
line-height: rem-calc(28);
|
||||
text-align: center;
|
||||
}
|
||||
.about-picture {
|
||||
padding: 70px 0;
|
||||
height: 226px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: $screen-md-min) {
|
||||
|
||||
.heading {
|
||||
.heading-title {
|
||||
h1 {
|
||||
font-size: rem-calc(16);
|
||||
padding: 26px 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
334
app/assets/stylesheets/app.utilities.scss
Normal file
@ -0,0 +1,334 @@
|
||||
.nopadding {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.no-upper {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.font-felt { font-family: $font-felt; }
|
||||
|
||||
p.font-felt {
|
||||
|
||||
}
|
||||
p, .widget p {
|
||||
&.font-felt, .font-felt { font-size: rem-calc(18) !important; }
|
||||
&.fleche-left {
|
||||
position: relative;
|
||||
padding-left: 5px;
|
||||
}
|
||||
img.fleche-left {
|
||||
position: absolute;
|
||||
left: -35px;
|
||||
top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.pos-rlt{position: relative;}
|
||||
.pos-stc{position: static;}
|
||||
.pos-abt{position: absolute;}
|
||||
.line {*width: 100%;height: 2px;margin: 10px 0;font-size:0;overflow: hidden;}
|
||||
.line-s{height: 1px;}
|
||||
.line-xs{margin: 0}
|
||||
.line-lg{margin-top:15px;margin-bottom: 15px}
|
||||
.line-dashed{border-style: dashed !important;background-color: transparent;border-width:0;}
|
||||
.no-line{border-width: 0}
|
||||
.no-border, .no-borders{border-color:transparent;border-width:0}
|
||||
.no-radius{border-radius: 0}
|
||||
.block{display:block;}
|
||||
.block.hide{display: none;}
|
||||
.inline{display:inline-block !important;}
|
||||
.none{display: none;}
|
||||
.pull-right-lg{float: right;}
|
||||
.pull-none{float: none;}
|
||||
.rounded{border-radius: 500px;}
|
||||
|
||||
.h480 { height: 480px; }
|
||||
|
||||
// .btn-s-xs{min-width: 90px}
|
||||
// .btn-s-sm{min-width: 100px}
|
||||
// .btn-s-md{min-width: 120px}
|
||||
// .btn-s-lg{min-width: 150px}
|
||||
// .btn-s-xl{min-width: 200px}
|
||||
|
||||
.l-h-2x{line-height: rem-calc(18);}
|
||||
// .l-h-1x{line-height: 1.2;}
|
||||
// .l-h{line-height: 1.5;}
|
||||
.l-n { line-height: 1em; }
|
||||
.v-middle{vertical-align: middle !important;}
|
||||
.v-top{vertical-align: top !important;}
|
||||
.v-bottom{vertical-align: bottom !important;}
|
||||
|
||||
.font-normal{font-weight: normal;}
|
||||
.font-thin{font-weight: 300;}
|
||||
.font-sbold{font-weight: 600;}
|
||||
.font-bold{font-weight: 700;}
|
||||
.font-ebold{font-weight: 900;}
|
||||
|
||||
.text-lg{font-size: $font-size-lg;}
|
||||
.text-md{font-size: $font-size-md;}
|
||||
.text-sm{font-size: $font-size-sm;}
|
||||
.text-xs{font-size: $font-size-xs;}
|
||||
.text-base{font-size: $font-size-base;}
|
||||
.text-ellipsis{
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
.text-u-c, .upper{text-transform: uppercase;}
|
||||
.text-l-t{text-decoration: line-through;}
|
||||
.text-u-l, .underline {text-decoration: underline;}
|
||||
.text-c { text-transform: capitalize; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
|
||||
.text-active, .active > .text, .active > .auto .text{display: none !important;}
|
||||
.active > .text-active, .active > .auto .text-active{display: inline-block !important;}
|
||||
.box-shadow{
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.cover { background-size:cover; }
|
||||
.break-word { word-wrap: break-word; }
|
||||
|
||||
.wrapper-xxs{padding: 4px 6px;}
|
||||
.wrapper-sm{padding: 5px 10px;}
|
||||
.wrapper{padding: 15px;}
|
||||
.wrapper-md{padding: 20px;}
|
||||
.wrapper-lg{padding: 30px;}
|
||||
.wrapper-xl{padding: 50px;}
|
||||
.padder{padding-left:15px;padding-right: 15px}
|
||||
.padder-icon{padding-left:30px;padding-right: 0px; padding-top: 3px; height: 21px}
|
||||
.padder-v{padding-top:15px;padding-bottom: 15px}
|
||||
.no-padder{padding: 0 !important;}
|
||||
.pull-in{margin-left: -15px;margin-right: -15px;}
|
||||
.pull-out{margin:-10px -15px;}
|
||||
|
||||
.width-70 { width: 70%; }
|
||||
|
||||
.b{border: 1px solid rgba(0, 0, 0, 0.05)}
|
||||
.b-a{border: 1px solid $border-color}
|
||||
.b-t{border-top: 1px solid $border-color}
|
||||
.b-r{border-right: 1px solid $border-color}
|
||||
.b-b{border-bottom: 1px solid $border-color}
|
||||
.b-l{border-left: 1px solid $border-color}
|
||||
.b-light{border-color: darken($brand-light, 5%)}
|
||||
.b-dark{border-color: lighten($brand-dark, 5%)}
|
||||
.b-primary{border-color: lighten($brand-primary, 5%)}
|
||||
.b-success{border-color: lighten($brand-success, 5%)}
|
||||
.b-info{border-color: lighten($brand-info, 5%)}
|
||||
.b-warning{border-color: lighten($brand-warning, 5%)}
|
||||
.b-danger{border-color: lighten($brand-danger, 5%)}
|
||||
.b-black{border-color: lighten($brand-black, 5%)}
|
||||
.b-white{border-color: #fff}
|
||||
.b-dashed{border-style: dashed !important;}
|
||||
|
||||
.b-2x{border-width: 2px}
|
||||
.b-3x{border-width: 3px}
|
||||
.b-4x{border-width: 4px}
|
||||
.b-5x{border-width: 5px}
|
||||
|
||||
.no-b { border: none !important; }
|
||||
|
||||
.r{
|
||||
border-radius: $border-radius-base $border-radius-base $border-radius-base $border-radius-base;
|
||||
}
|
||||
.r-l{
|
||||
border-radius: $border-radius-base 0 0 $border-radius-base;
|
||||
}
|
||||
|
||||
.r-r{
|
||||
border-radius: 0 $border-radius-base $border-radius-base 0;
|
||||
}
|
||||
|
||||
.r-t{
|
||||
border-radius: $border-radius-base $border-radius-base 0 0;
|
||||
}
|
||||
|
||||
.r-b{
|
||||
border-radius: 0 0 $border-radius-base $border-radius-base;
|
||||
}
|
||||
.r-n { border-radius: 0 0 0 0; }
|
||||
|
||||
.p-lg { padding: 30px; }
|
||||
.p-l { padding: 16px; }
|
||||
|
||||
.m-xxs{margin: 2px 4px}
|
||||
.m-xs{margin: 5px;}
|
||||
.m-sm{margin: 10px;}
|
||||
.m{margin: 15px;}
|
||||
.m-md{margin: 20px;}
|
||||
.m-lg{margin: 30px;}
|
||||
.m-xl{margin: 50px;}
|
||||
.m-n{margin: 0 !important}
|
||||
|
||||
.m-l-none{margin-left: 0}
|
||||
.m-l-xs{margin-left: 5px;}
|
||||
.m-l-sm{margin-left: 10px;}
|
||||
.m-l{margin-left: 15px}
|
||||
.m-l-md{margin-left: 20px;}
|
||||
.m-l-lg{margin-left: 30px;}
|
||||
.m-l-xl{margin-left: 40px;}
|
||||
|
||||
.m-l-n-xxs{margin-left: -1px}
|
||||
.m-l-n-xs{margin-left: -5px}
|
||||
.m-l-n-sm{margin-left: -10px}
|
||||
.m-l-n{margin-left: -15px}
|
||||
.m-l-n-md{margin-left: -20px}
|
||||
.m-l-n-lg{margin-left: -30px}
|
||||
.m-l-n-xl{margin-left: -40px}
|
||||
|
||||
.m-t-none{margin-top:0}
|
||||
.m-t-xxs{margin-top: 1px;}
|
||||
.m-t-xs{margin-top: 5px;}
|
||||
.m-t-sm{margin-top: 10px;}
|
||||
.m-t{margin-top: 15px}
|
||||
.m-t-md{margin-top: 20px;}
|
||||
.m-t-lg{margin-top: 30px;}
|
||||
.m-t-xl{margin-top: 40px;}
|
||||
|
||||
.m-t-n-xxs{margin-top: -1px}
|
||||
.m-t-n-xs{margin-top: -5px}
|
||||
.m-t-n-sm{margin-top: -10px}
|
||||
.m-t-n{margin-top: -15px}
|
||||
.m-t-n-md{margin-top: -20px}
|
||||
.m-t-n-lg{margin-top: -30px}
|
||||
.m-t-n-xl{margin-top: -40px}
|
||||
|
||||
.m-r-none{margin-right: 0}
|
||||
.m-r-xxs{margin-right: 1px}
|
||||
.m-r-xs{margin-right: 5px}
|
||||
.m-r-sm{margin-right: 10px}
|
||||
.m-r{margin-right: 15px}
|
||||
.m-r-md{margin-right: 20px}
|
||||
.m-r-lg{margin-right: 30px}
|
||||
.m-r-xl{margin-right: 40px}
|
||||
|
||||
.m-r-n-xxs{margin-right: -1px}
|
||||
.m-r-n-xs{margin-right: -5px}
|
||||
.m-r-n-sm{margin-right: -10px}
|
||||
.m-r-n{margin-right: -15px}
|
||||
.m-r-n-md{margin-right: -20px}
|
||||
.m-r-n-lg{margin-right: -30px}
|
||||
.m-r-n-xl{margin-right: -40px}
|
||||
|
||||
.m-b-none{margin-bottom: 0}
|
||||
.m-b-xxs{margin-bottom: 1px;}
|
||||
.m-b-xs{margin-bottom: 5px;}
|
||||
.m-b-sm{margin-bottom: 10px;}
|
||||
.m-b{margin-bottom: 15px;}
|
||||
.m-b-md{margin-bottom: 20px;}
|
||||
.m-b-lg{margin-bottom: 30px;}
|
||||
.m-b-xl{margin-bottom: 40px;}
|
||||
|
||||
.m-b-n-xxs{margin-bottom: -1px}
|
||||
.m-b-n-xs{margin-bottom: -5px}
|
||||
.m-b-n-sm{margin-bottom: -10px}
|
||||
.m-b-n{margin-bottom: -15px}
|
||||
.m-b-n-md{margin-bottom: -20px}
|
||||
.m-b-n-lg{margin-bottom: -30px}
|
||||
.m-b-n-xl{margin-bottom: -40px}
|
||||
|
||||
.media-xs{min-width: 50px}
|
||||
.media-sm{min-width: 80px}
|
||||
.media-md{min-width: 90px}
|
||||
.media-lg{min-width: 120px}
|
||||
|
||||
.thumb-38 { width: 38px !important; height: 38px; }
|
||||
.thumb-50 { width: 50px !important; height: 50px; }
|
||||
.thumb-128 { width: 128px !important; height: 128px; }
|
||||
.thumb-128-wrapper {
|
||||
img {
|
||||
width: 128px !important; height: 128px;
|
||||
@extend .img-circle;
|
||||
}
|
||||
}
|
||||
|
||||
.thumb-lg{width: 128px;display: inline-block}
|
||||
.thumb-md{width: 64px;display: inline-block}
|
||||
.thumb{width: 50px;display: inline-block}
|
||||
.thumb-sm{width: 34px;display: inline-block}
|
||||
.thumb-xs{width: 24px;display: inline-block}
|
||||
.thumb-wrapper{padding: 2px; border: 1px solid #ddd}
|
||||
.thumb img,
|
||||
.thumb-xs img,
|
||||
.thumb-sm img,
|
||||
.thumb-md img,
|
||||
.thumb-lg img,
|
||||
.thumb-btn img{
|
||||
// height: auto;
|
||||
// max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.img-full{
|
||||
max-width: 100%;
|
||||
> img{
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
.clear{display:block;overflow: hidden;}
|
||||
|
||||
.scroll-x, .scroll-y{overflow:hidden;-webkit-overflow-scrolling:touch;}
|
||||
.scroll-y{overflow-y:auto;}
|
||||
.scroll-x{overflow-x:auto;}
|
||||
.no-touch {
|
||||
.scroll-x,
|
||||
.scroll-y{
|
||||
overflow: hidden;
|
||||
}
|
||||
.scroll-x{
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active{
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
.scroll-y{
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active{
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.hover-action{
|
||||
display: none;
|
||||
}
|
||||
.hover:hover {
|
||||
.hover-action{
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $screen-lg-min) {
|
||||
.b-r-lg {border-right: 1px solid $border-color; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*desktop*/
|
||||
@media screen and (min-width: $screen-md-min) {
|
||||
.b-r-md {border-right: 1px solid $border-color; }
|
||||
.col-lg-2-4{width: 20.000%;float: left;}
|
||||
|
||||
.hide-b-md { border: none; }
|
||||
}
|
||||
|
||||
/*phone*/
|
||||
@media (max-width: 767px) {
|
||||
.shift{display: none !important;}
|
||||
.shift.in{display: block !important;}
|
||||
.row-2 [class*="col"]{width: 50%;float: left}
|
||||
.row-2 .col-0{clear: none}
|
||||
.row-2 li:nth-child(odd) { clear: left;margin-left: 0}
|
||||
.text-center-xs{text-align: center;}
|
||||
.text-left-xs{text-align: left;}
|
||||
.pull-none-xs{float: none !important;}
|
||||
.dropdown-menu.pull-none-xs{left: 0;}
|
||||
.hidden-xs.show{display: inherit !important;}
|
||||
.wrapper-lg{padding: 15px;}
|
||||
}
|
||||
|
||||
|
27
app/assets/stylesheets/application.scss
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
*= require_self
|
||||
*= require select2/select2
|
||||
*= require jasny-bootstrap/dist/css/jasny-bootstrap
|
||||
*= require angular-growl/build/angular-growl.min.css
|
||||
*= require angular-xeditable/dist/css/xeditable
|
||||
*= require redactor
|
||||
*= require angular-loading-bar/src/loading-bar
|
||||
*= require font-awesome
|
||||
*/
|
||||
|
||||
@import "app.functions";
|
||||
@import "compass";
|
||||
@import "bootstrap_and_overrides";
|
||||
|
||||
@import "app.utilities";
|
||||
@import "app.colors";
|
||||
|
||||
@import "app.base";
|
||||
@import "app.layout";
|
||||
@import "app.nav";
|
||||
|
||||
@import "app.buttons";
|
||||
@import "app.components";
|
||||
@import "app.plugins";
|
||||
|
||||
@import "app.responsive";
|
979
app/assets/stylesheets/bootstrap_and_overrides.scss
vendored
Normal file
@ -0,0 +1,979 @@
|
||||
// a flag to toggle asset pipeline / compass integration
|
||||
// defaults to true if twbs-font-path function is present (no function => twbs-font-path('') parsed as string == right side)
|
||||
// in Sass 3.3 this can be improved with: function-exists(twbs-font-path)
|
||||
$bootstrap-sass-asset-helper: (twbs-font-path("") != unquote('twbs-font-path("")')) !default;
|
||||
//
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
|
||||
$gray-darker: lighten(#000, 13.5%) !default; // #222
|
||||
$gray-dark: lighten(#000, 20%) !default; // #333
|
||||
$gray: lighten(#000, 33.5%) !default; // #555
|
||||
$gray-light: lighten(#000, 60%) !default; // #999
|
||||
$gray-lighter: lighten(#000, 93.5%) !default; // #eee
|
||||
|
||||
$brand-primary: #d92227 !default;
|
||||
$brand-success: #7bca38 !default;
|
||||
$brand-info: #1d98ec !default;
|
||||
$brand-warning: #fdde3f !default;
|
||||
$brand-danger: $brand-primary !default;
|
||||
|
||||
|
||||
// ADD by sleede ------------------
|
||||
$brand-black: #12131a;
|
||||
$brand-dark: #222733;
|
||||
$brand-light: #f4f3f3; //#f1f3f7
|
||||
$brand-inverse: #433599;
|
||||
|
||||
$black-light: #444444;
|
||||
|
||||
$bg-gray: #f5f5f5;
|
||||
|
||||
$red-dark: #cb1117;
|
||||
$red: $red-dark;
|
||||
$red-light: #e13f45;
|
||||
|
||||
$yellow: #fd0;
|
||||
$blue: $brand-info;
|
||||
$green: $brand-success;
|
||||
$beige: #e4cd78;
|
||||
$violet: #bd7ae9;
|
||||
|
||||
$border-color: #dddddd;
|
||||
$header-bg: $bg-gray;
|
||||
|
||||
$off-screen-nav-width: 75%;
|
||||
$nav-primary-height: 36px;
|
||||
$nav-xs-width: 90px;
|
||||
$nav-xs-height: 50px;
|
||||
$header-md-height: 73px;
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
//== Scaffolding
|
||||
//
|
||||
//## Settings for some of the most global styles.
|
||||
|
||||
//** Background color for `<body>`.
|
||||
$body-bg: #fff !default;
|
||||
//** Global text color on `<body>`.
|
||||
$text-color: black !default;
|
||||
|
||||
//** Global textual link color.
|
||||
$link-color: #cb1117 !default;
|
||||
//** Link hover color set via `darken()` function.
|
||||
$link-hover-color: #840b0f !default;
|
||||
//** Link hover decoration.
|
||||
$link-hover-decoration: underline;
|
||||
|
||||
//== Typography
|
||||
//
|
||||
//## Font, line-height, and color for body text, headings, and more.
|
||||
|
||||
// Semibold = 600, Bold = 700, ExtraB = 800
|
||||
|
||||
$font-family-sans-serif: "proxima-nova", Helvetica, Arial, sans-serif !default;
|
||||
$font-proxima-condensed: "proxima-nova-condensed", Helvetica, Arial, sans-serif !default;
|
||||
$font-family-serif: Georgia, "Times New Roman", Times, serif !default;
|
||||
$font-felt: "felt-tip-roman", sans-serif;
|
||||
|
||||
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
|
||||
// $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default;
|
||||
|
||||
$font-family-base: $font-family-sans-serif !default;
|
||||
$font-family-monospace: $font-family-base;
|
||||
|
||||
$font-size-base: 16px !default;
|
||||
$font-size-large: ceil(($font-size-base * 1.125)) !default; // ~18px
|
||||
$font-size-medium: ceil(($font-size-base * 0.875)) !default; // ~14px
|
||||
$font-size-small: ceil(($font-size-base * 0.75)) !default; // ~12px
|
||||
|
||||
//add sleede
|
||||
$font-size-lg: rem-calc(20);
|
||||
$font-size-md: rem-calc(18);
|
||||
$font-size-sm: rem-calc(14);
|
||||
$font-size-xs: rem-calc(12); // ~11px
|
||||
|
||||
$font-size-h1: rem-calc(24) !default; // ~24px
|
||||
$font-size-h2: rem-calc(20) !default; // ~20px
|
||||
$font-size-h3: rem-calc(18) !default; // ~18px
|
||||
$font-size-h4: rem-calc(16) !default; // ~16px
|
||||
$font-size-h5: rem-calc(14) !default; // ~14px
|
||||
$font-size-h6: rem-calc(12) !default; // ~12px
|
||||
|
||||
|
||||
|
||||
//** Unit-less `line-height` for use in components like buttons.
|
||||
$line-height-base: 1.5 !default; // 24/16
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
$line-height-computed: floor(($font-size-base * $line-height-base)) !default; // ~20px
|
||||
|
||||
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
|
||||
$line-height-computed: floor(($font-size-base * $line-height-base)) !default; // ~20px
|
||||
|
||||
//** By default, this inherits from the `<body>`.
|
||||
$headings-font-family: $font-family-base !default;
|
||||
$headings-font-weight: 400 !default;
|
||||
$headings-line-height: 1.5 !default;
|
||||
$headings-color: black !default;
|
||||
|
||||
|
||||
//== Iconography
|
||||
//
|
||||
//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
|
||||
|
||||
//** Load fonts from this directory.
|
||||
$icon-font-path: "bootstrap/" !default;
|
||||
//** File name for all font files.
|
||||
$icon-font-name: "glyphicons-halflings-regular" !default;
|
||||
//** Element ID within SVG icon file.
|
||||
$icon-font-svg-id: "glyphicons_halflingsregular" !default;
|
||||
|
||||
$icon-css-prefix: fa;
|
||||
|
||||
|
||||
//== Components
|
||||
//
|
||||
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
|
||||
|
||||
$padding-base-vertical: 6px !default;
|
||||
$padding-base-horizontal: 12px !default;
|
||||
|
||||
$padding-large-vertical: 10px !default;
|
||||
$padding-large-horizontal: 16px !default;
|
||||
|
||||
$padding-small-vertical: 5px !default;
|
||||
$padding-small-horizontal: 10px !default;
|
||||
|
||||
$padding-xs-vertical: 1px !default;
|
||||
$padding-xs-horizontal: 5px !default;
|
||||
|
||||
$line-height-large: 1.33 !default;
|
||||
$line-height-small: 1.5 !default;
|
||||
|
||||
$border-radius-base: 4px !default;
|
||||
$border-radius-large: 6px !default;
|
||||
$border-radius-small: 3px !default;
|
||||
|
||||
//** Global color for active items (e.g., navs or dropdowns).
|
||||
$component-active-color: #fff !default;
|
||||
//** Global background color for active items (e.g., navs or dropdowns).
|
||||
$component-active-bg: $gray-lighter !default;
|
||||
|
||||
//** Width of the `border` for generating carets that indicator dropdowns.
|
||||
$caret-width-base: 4px !default;
|
||||
//** Carets increase slightly in size for larger components.
|
||||
$caret-width-large: 5px !default;
|
||||
|
||||
|
||||
//== Tables
|
||||
//
|
||||
//## Customizes the `.table` component with basic values, each used across all table variations.
|
||||
|
||||
//** Padding for `<th>`s and `<td>`s.
|
||||
$table-cell-padding: 8px !default;
|
||||
//** Padding for cells in `.table-condensed`.
|
||||
$table-condensed-cell-padding: 5px !default;
|
||||
|
||||
//** Default background color used for all tables.
|
||||
$table-bg: transparent !default;
|
||||
//** Background color used for `.table-striped`.
|
||||
$table-bg-accent: #f9f9f9 !default;
|
||||
//** Background color used for `.table-hover`.
|
||||
$table-bg-hover: $bg-gray !default;
|
||||
$table-bg-active: $table-bg-hover !default;
|
||||
|
||||
//** Border color for table and cell borders.
|
||||
$table-border-color: #ddd !default;
|
||||
|
||||
|
||||
//== Buttons
|
||||
//
|
||||
//## For each of Bootstrap's buttons, define text, background and border color.
|
||||
|
||||
$btn-font-weight: normal !default;
|
||||
|
||||
$btn-default-color: $text-color !default;
|
||||
$btn-default-bg: lighten($brand-light, 3%) !default;
|
||||
$btn-default-border: darken($border-color, 8%) !default;
|
||||
|
||||
$btn-primary-color: #fff !default;;
|
||||
$btn-primary-bg: $brand-primary !default;;
|
||||
$btn-primary-border: $btn-primary-bg !default;;
|
||||
|
||||
$btn-success-color: #fff !default;
|
||||
$btn-success-bg: $brand-success !default;
|
||||
$btn-success-border: $btn-success-bg !default;
|
||||
|
||||
$btn-info-color: #fff !default;
|
||||
$btn-info-bg: $brand-info !default;
|
||||
$btn-info-border: $btn-info-bg !default;
|
||||
|
||||
$btn-warning-color: #000 !default;
|
||||
$btn-warning-bg: $brand-warning !default;
|
||||
$btn-warning-border: $btn-warning-bg !default;
|
||||
|
||||
$btn-danger-color: #fff !default;
|
||||
$btn-danger-bg: $red !default;
|
||||
$btn-danger-border: $btn-default-border !default;
|
||||
|
||||
$btn-link-disabled-color: $gray-light !default;
|
||||
|
||||
|
||||
|
||||
//== Forms
|
||||
//
|
||||
//##
|
||||
|
||||
//** `<input>` background color
|
||||
$input-bg: #fff !default;
|
||||
//** `<input disabled>` background color
|
||||
$input-bg-disabled: $gray-lighter !default;
|
||||
|
||||
//** Text color for `<input>`s
|
||||
$input-color: $gray !default;
|
||||
//** `<input>` border color
|
||||
$input-border: darken($border-color, 10%) !default;
|
||||
//** `<input>` border radius
|
||||
$input-border-radius: $border-radius-base !default;
|
||||
//** Border color for inputs on focus
|
||||
$input-border-focus: $brand-warning !default;
|
||||
//** Large `.form-control` border radius
|
||||
$input-border-radius-large: $border-radius-large;
|
||||
//** Small `.form-control` border radius
|
||||
$input-border-radius-small: $border-radius-small;
|
||||
|
||||
//** Placeholder text color
|
||||
$input-color-placeholder: $gray-light !default;
|
||||
|
||||
//** Default `.form-control` height
|
||||
$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
|
||||
//** Large `.form-control` height
|
||||
$input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
|
||||
//** Small `.form-control` height
|
||||
$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
|
||||
|
||||
$legend-color: $gray-dark !default;
|
||||
$legend-border-color: #e5e5e5 !default;
|
||||
|
||||
//** Background color for textual input addons
|
||||
$input-group-addon-bg: $gray-lighter !default;
|
||||
//** Border color for textual input addons
|
||||
$input-group-addon-border-color: $input-border !default;
|
||||
//** Disabled cursor for form controls and buttons.
|
||||
$cursor-disabled: not-allowed;
|
||||
$form-group-margin-bottom: 15px !default;
|
||||
|
||||
//== Dropdowns
|
||||
//
|
||||
//## Dropdown menu container and contents.
|
||||
|
||||
//** Background for the dropdown menu.
|
||||
$dropdown-bg: #fff !default;
|
||||
//** Dropdown menu `border-color`.
|
||||
$dropdown-border: rgba(0,0,0,.15) !default;
|
||||
//** Dropdown menu `border-color` **for IE8**.
|
||||
$dropdown-fallback-border: #ccc !default;
|
||||
//** Divider color for between dropdown items.
|
||||
$dropdown-divider-bg: #e5e5e5 !default;
|
||||
|
||||
//** Dropdown link text color.
|
||||
$dropdown-link-color: $gray-dark !default;
|
||||
//** Hover color for dropdown links.
|
||||
$dropdown-link-hover-color: darken($gray-dark, 5%) !default;
|
||||
//** Hover background for dropdown links.
|
||||
$dropdown-link-hover-bg: $bg-gray !default;
|
||||
|
||||
//** Active dropdown menu item text color.
|
||||
$dropdown-link-active-color: $component-active-color !default;
|
||||
//** Active dropdown menu item background color.
|
||||
$dropdown-link-active-bg: $component-active-bg !default;
|
||||
|
||||
//** Disabled dropdown menu item background color.
|
||||
$dropdown-link-disabled-color: $gray-light !default;
|
||||
|
||||
//** Text color for headers within dropdown menus.
|
||||
$dropdown-header-color: $gray-light !default;
|
||||
|
||||
//** Deprecated `$dropdown-caret-color` as of v3.1.0
|
||||
$dropdown-caret-color: #000 !default;
|
||||
|
||||
|
||||
//-- Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
//
|
||||
// Note: These variables are not generated into the Customizer.
|
||||
|
||||
$zindex-navbar: 1000 !default;
|
||||
$zindex-dropdown: 1000 !default;
|
||||
$zindex-popover: 1060 !default;
|
||||
$zindex-tooltip: 1070 !default;
|
||||
$zindex-navbar-fixed: 1030 !default;
|
||||
$zindex-modal-background: 1040 !default;
|
||||
$zindex-modal: 1050 !default;
|
||||
|
||||
|
||||
//== Media queries breakpoints
|
||||
//
|
||||
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
|
||||
|
||||
// Extra small screen / phone
|
||||
//** Deprecated `$screen-xs` as of v3.0.1
|
||||
$screen-xs: 480px !default;
|
||||
//** Deprecated `$screen-xs-min` as of v3.2.0
|
||||
$screen-xs-min: $screen-xs !default;
|
||||
//** Deprecated `$screen-phone` as of v3.0.1
|
||||
$screen-phone: $screen-xs-min !default;
|
||||
|
||||
// Small screen / tablet
|
||||
//** Deprecated `$screen-sm` as of v3.0.1
|
||||
$screen-sm: 768px !default;
|
||||
$screen-sm-min: $screen-sm !default;
|
||||
//** Deprecated `$screen-tablet` as of v3.0.1
|
||||
$screen-tablet: $screen-sm-min !default;
|
||||
|
||||
// Medium screen / desktop
|
||||
//** Deprecated `$screen-md` as of v3.0.1
|
||||
$screen-md: 992px !default;
|
||||
$screen-md-min: $screen-md !default;
|
||||
//** Deprecated `$screen-desktop` as of v3.0.1
|
||||
$screen-desktop: $screen-md-min !default;
|
||||
|
||||
// Large screen / wide desktop
|
||||
//** Deprecated `$screen-lg` as of v3.0.1
|
||||
$screen-lg: 1200px !default;
|
||||
$screen-lg-min: $screen-lg !default;
|
||||
//** Deprecated `$screen-lg-desktop` as of v3.0.1
|
||||
$screen-lg-desktop: $screen-lg-min !default;
|
||||
|
||||
// So media queries don't overlap when required, provide a maximum
|
||||
$screen-xs-max: ($screen-sm-min - 1) !default;
|
||||
$screen-sm-max: ($screen-md-min - 1) !default;
|
||||
$screen-md-max: ($screen-lg-min - 1) !default;
|
||||
|
||||
|
||||
//== Grid system
|
||||
//
|
||||
//## Define your custom responsive grid.
|
||||
|
||||
//** Number of columns in the grid.
|
||||
$grid-columns: 12 !default;
|
||||
//** Padding between columns. Gets divided in half for the left and right.
|
||||
$grid-gutter-width: 30px !default;
|
||||
// Navbar collapse
|
||||
//** Point at which the navbar becomes uncollapsed.
|
||||
$grid-float-breakpoint: $screen-sm-min !default;
|
||||
//** Point at which the navbar begins collapsing.
|
||||
$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
|
||||
|
||||
|
||||
//== Container sizes
|
||||
//
|
||||
//## Define the maximum width of `.container` for different screen sizes.
|
||||
|
||||
// Small screen / tablet
|
||||
$container-tablet: ((720px + $grid-gutter-width)) !default;
|
||||
//** For `$screen-sm-min` and up.
|
||||
$container-sm: $container-tablet !default;
|
||||
|
||||
// Medium screen / desktop
|
||||
$container-desktop: ((940px + $grid-gutter-width)) !default;
|
||||
//** For `$screen-md-min` and up.
|
||||
$container-md: $container-desktop !default;
|
||||
|
||||
// Large screen / wide desktop
|
||||
$container-large-desktop: ((1140px + $grid-gutter-width)) !default;
|
||||
//** For `$screen-lg-min` and up.
|
||||
$container-lg: $container-large-desktop !default;
|
||||
|
||||
|
||||
//== Navbar
|
||||
//
|
||||
//##
|
||||
|
||||
// Basics of a navbar
|
||||
$navbar-height: 50px !default;
|
||||
$navbar-margin-bottom: $line-height-computed !default;
|
||||
$navbar-border-radius: $border-radius-base !default;
|
||||
$navbar-padding-horizontal: floor(($grid-gutter-width / 2)) !default;
|
||||
$navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default;
|
||||
$navbar-collapse-max-height: 340px !default;
|
||||
|
||||
$navbar-default-color: #777 !default;
|
||||
$navbar-default-bg: #f8f8f8 !default;
|
||||
$navbar-default-border: darken($navbar-default-bg, 6.5%) !default;
|
||||
|
||||
// Navbar links
|
||||
$navbar-default-link-color: #777 !default;
|
||||
$navbar-default-link-hover-color: #333 !default;
|
||||
$navbar-default-link-hover-bg: transparent !default;
|
||||
$navbar-default-link-active-color: #555 !default;
|
||||
$navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%) !default;
|
||||
$navbar-default-link-disabled-color: #ccc !default;
|
||||
$navbar-default-link-disabled-bg: transparent !default;
|
||||
|
||||
// Navbar brand label
|
||||
$navbar-default-brand-color: $navbar-default-link-color !default;
|
||||
$navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) !default;
|
||||
$navbar-default-brand-hover-bg: transparent !default;
|
||||
|
||||
// Navbar toggle
|
||||
$navbar-default-toggle-hover-bg: #ddd !default;
|
||||
$navbar-default-toggle-icon-bar-bg: #888 !default;
|
||||
$navbar-default-toggle-border-color: #ddd !default;
|
||||
|
||||
|
||||
// Inverted navbar
|
||||
// Reset inverted navbar basics
|
||||
$navbar-inverse-color: $gray-light !default;
|
||||
$navbar-inverse-bg: #222 !default;
|
||||
$navbar-inverse-border: darken($navbar-inverse-bg, 10%) !default;
|
||||
|
||||
// Inverted navbar links
|
||||
$navbar-inverse-link-color: $gray-light !default;
|
||||
$navbar-inverse-link-hover-color: #fff !default;
|
||||
$navbar-inverse-link-hover-bg: transparent !default;
|
||||
$navbar-inverse-link-active-color: $navbar-inverse-link-hover-color !default;
|
||||
$navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%) !default;
|
||||
$navbar-inverse-link-disabled-color: #444 !default;
|
||||
$navbar-inverse-link-disabled-bg: transparent !default;
|
||||
|
||||
// Inverted navbar brand label
|
||||
$navbar-inverse-brand-color: $navbar-inverse-link-color !default;
|
||||
$navbar-inverse-brand-hover-color: #fff !default;
|
||||
$navbar-inverse-brand-hover-bg: transparent !default;
|
||||
|
||||
// Inverted navbar toggle
|
||||
$navbar-inverse-toggle-hover-bg: #333 !default;
|
||||
$navbar-inverse-toggle-icon-bar-bg: #fff !default;
|
||||
$navbar-inverse-toggle-border-color: #333 !default;
|
||||
|
||||
|
||||
//== Navs
|
||||
//
|
||||
//##
|
||||
|
||||
//=== Shared nav styles
|
||||
$nav-link-padding: 10px 15px !default;
|
||||
$nav-link-hover-bg: $gray-lighter !default;
|
||||
|
||||
$nav-disabled-link-color: $gray-light !default;
|
||||
$nav-disabled-link-hover-color: $gray-light !default;
|
||||
|
||||
$nav-open-link-hover-color: #fff !default;
|
||||
|
||||
//== Tabs
|
||||
$nav-tabs-border-color: #ddd !default;
|
||||
|
||||
$nav-tabs-link-hover-border-color: $gray-lighter !default;
|
||||
|
||||
$nav-tabs-active-link-hover-bg: $body-bg !default;
|
||||
$nav-tabs-active-link-hover-color: $gray !default;
|
||||
$nav-tabs-active-link-hover-border-color: #ddd !default;
|
||||
|
||||
$nav-tabs-justified-link-border-color: #ddd !default;
|
||||
$nav-tabs-justified-active-link-border-color: $body-bg !default;
|
||||
|
||||
//== Pills
|
||||
$nav-pills-border-radius: $border-radius-base !default;
|
||||
$nav-pills-active-link-hover-bg: $component-active-bg !default;
|
||||
$nav-pills-active-link-hover-color: $component-active-color !default;
|
||||
|
||||
|
||||
//== Pagination
|
||||
//
|
||||
//##
|
||||
|
||||
$pagination-color: $link-color !default;
|
||||
$pagination-bg: #fff !default;
|
||||
$pagination-border: #ddd !default;
|
||||
|
||||
$pagination-hover-color: $link-hover-color !default;
|
||||
$pagination-hover-bg: $gray-lighter !default;
|
||||
$pagination-hover-border: #ddd !default;
|
||||
|
||||
$pagination-active-color: #fff !default;
|
||||
$pagination-active-bg: $brand-primary !default;
|
||||
$pagination-active-border: $brand-primary !default;
|
||||
|
||||
$pagination-disabled-color: $gray-light !default;
|
||||
$pagination-disabled-bg: #fff !default;
|
||||
$pagination-disabled-border: #ddd !default;
|
||||
|
||||
|
||||
//== Pager
|
||||
//
|
||||
//##
|
||||
|
||||
$pager-bg: $pagination-bg !default;
|
||||
$pager-border: $pagination-border !default;
|
||||
$pager-border-radius: 15px !default;
|
||||
|
||||
$pager-hover-bg: $pagination-hover-bg !default;
|
||||
|
||||
$pager-active-bg: $pagination-active-bg !default;
|
||||
$pager-active-color: $pagination-active-color !default;
|
||||
|
||||
$pager-disabled-color: $pagination-disabled-color !default;
|
||||
|
||||
|
||||
//== Jumbotron
|
||||
//
|
||||
//##
|
||||
|
||||
$jumbotron-padding: 30px !default;
|
||||
$jumbotron-color: inherit !default;
|
||||
$jumbotron-bg: $gray-lighter !default;
|
||||
$jumbotron-heading-color: inherit !default;
|
||||
$jumbotron-font-size: ceil(($font-size-base * 1.5)) !default;
|
||||
|
||||
|
||||
//== Form states and alerts
|
||||
//
|
||||
//## Define colors for form feedback states and, by default, alerts.
|
||||
|
||||
$state-success-text: #3c763d !default;
|
||||
$state-success-bg: #dff0d8 !default;
|
||||
$state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default;
|
||||
|
||||
$state-info-text: #31708f !default;
|
||||
$state-info-bg: #d9edf7 !default;
|
||||
$state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default;
|
||||
|
||||
$state-warning-text: #8a6d3b !default;
|
||||
$state-warning-bg: #fcf8e3 !default;
|
||||
$state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default;
|
||||
|
||||
$state-danger-text: #a94442 !default;
|
||||
$state-danger-bg: #f2dede !default;
|
||||
$state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default;
|
||||
|
||||
|
||||
//== Tooltips
|
||||
//
|
||||
//##
|
||||
|
||||
//** Tooltip max width
|
||||
$tooltip-max-width: 200px !default;
|
||||
//** Tooltip text color
|
||||
$tooltip-color: white !default;
|
||||
//** Tooltip background color
|
||||
$tooltip-bg: #424242 !default;
|
||||
$tooltip-opacity: .9 !default;
|
||||
|
||||
//** Tooltip arrow width
|
||||
$tooltip-arrow-width: 5px !default;
|
||||
//** Tooltip arrow color
|
||||
$tooltip-arrow-color: $tooltip-bg !default;
|
||||
|
||||
//add sleede
|
||||
$tooltip-fallback-color: rgba(0,0,0,.9) !default;
|
||||
|
||||
//== Arrow
|
||||
// add by sleede
|
||||
//##
|
||||
$arrow-width: 7px;
|
||||
$arrow-color: #fff;
|
||||
$arrow-outer-width: ($arrow-width + 1);
|
||||
$arrow-outer-color: rgba(0,0,0,.1);
|
||||
$arrow-outer-fallback-color: #eee;
|
||||
|
||||
|
||||
//== Popovers
|
||||
//
|
||||
//##
|
||||
|
||||
//** Popover body background color
|
||||
$popover-bg: #fff !default;
|
||||
//** Popover maximum width
|
||||
$popover-max-width: 276px !default;
|
||||
//** Popover border color
|
||||
$popover-border-color: rgba(0,0,0,.2) !default;
|
||||
//** Popover fallback border color
|
||||
$popover-fallback-border-color: #ccc !default;
|
||||
|
||||
//** Popover title background color
|
||||
$popover-title-bg: darken($popover-bg, 3%) !default;
|
||||
|
||||
//** Popover arrow width
|
||||
$popover-arrow-width: 10px !default;
|
||||
//** Popover arrow color
|
||||
$popover-arrow-color: #fff !default;
|
||||
|
||||
//** Popover outer arrow width
|
||||
$popover-arrow-outer-width: ($popover-arrow-width + 1) !default;
|
||||
//** Popover outer arrow color
|
||||
$popover-arrow-outer-color: fade_in($popover-border-color, 0.05) !default;
|
||||
//** Popover outer arrow fallback color
|
||||
$popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) !default;
|
||||
|
||||
|
||||
//== Labels
|
||||
//
|
||||
//##
|
||||
|
||||
//** Default label background color
|
||||
$label-default-bg: $gray-light !default;
|
||||
//** Primary label background color
|
||||
$label-primary-bg: $brand-primary !default;
|
||||
//** Success label background color
|
||||
$label-success-bg: $brand-success !default;
|
||||
//** Info label background color
|
||||
$label-info-bg: $brand-info !default;
|
||||
//** Warning label background color
|
||||
$label-warning-bg: $brand-warning !default;
|
||||
//** Danger label background color
|
||||
$label-danger-bg: $brand-danger !default;
|
||||
|
||||
//** Default label text color
|
||||
$label-color: #424242 !default;
|
||||
//** Default text color of a linked label
|
||||
$label-link-hover-color: $red !default;
|
||||
|
||||
|
||||
//== Modals
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding applied to the modal body
|
||||
$modal-inner-padding: 15px !default;
|
||||
|
||||
//** Padding applied to the modal title
|
||||
$modal-title-padding: 8px !default;
|
||||
//** Modal title line-height
|
||||
$modal-title-line-height: $line-height-base !default;
|
||||
|
||||
//** Background color of modal content area
|
||||
$modal-content-bg: #fff !default;
|
||||
//** Modal content border color
|
||||
$modal-content-border-color: rgba(0,0,0,.2) !default;
|
||||
//** Modal content border color **for IE8**
|
||||
$modal-content-fallback-border-color: #999 !default;
|
||||
|
||||
//** Modal backdrop background color
|
||||
$modal-backdrop-bg: #000 !default;
|
||||
//** Modal backdrop opacity
|
||||
$modal-backdrop-opacity: .9 !default;
|
||||
//** Modal header border color
|
||||
$modal-header-border-color: #e5e5e5 !default;
|
||||
//** Modal footer border color
|
||||
$modal-footer-border-color: $modal-header-border-color !default;
|
||||
|
||||
$modal-lg: 600px !default;
|
||||
$modal-md: 440px !default;
|
||||
$modal-sm: 340px !default;
|
||||
|
||||
|
||||
//== Alerts
|
||||
//
|
||||
//## Define alert colors, border radius, and padding.
|
||||
|
||||
$alert-padding: 15px !default;
|
||||
$alert-border-radius: $border-radius-base !default;
|
||||
$alert-link-font-weight: bold !default;
|
||||
|
||||
$alert-success-bg: $state-success-bg !default;
|
||||
$alert-success-text: $state-success-text !default;
|
||||
$alert-success-border: $state-success-border !default;
|
||||
|
||||
$alert-info-bg: $state-info-bg !default;
|
||||
$alert-info-text: $state-info-text !default;
|
||||
$alert-info-border: $state-info-border !default;
|
||||
|
||||
$alert-warning-bg: $state-warning-bg !default;
|
||||
$alert-warning-text: $state-warning-text !default;
|
||||
$alert-warning-border: $state-warning-border !default;
|
||||
|
||||
$alert-danger-bg: $state-danger-bg !default;
|
||||
$alert-danger-text: $state-danger-text !default;
|
||||
$alert-danger-border: $state-danger-border !default;
|
||||
|
||||
|
||||
//== Progress bars
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color of the whole progress component
|
||||
$progress-bg: $bg-gray !default;
|
||||
//** Progress bar text color
|
||||
$progress-bar-color: #fff !default;
|
||||
//** Variable for setting rounded corners on progress bar.
|
||||
$progress-border-radius: $border-radius-base;
|
||||
|
||||
//** Default progress bar color
|
||||
$progress-bar-bg: $brand-primary !default;
|
||||
//** Success progress bar color
|
||||
$progress-bar-success-bg: $brand-success !default;
|
||||
//** Warning progress bar color
|
||||
$progress-bar-warning-bg: $brand-warning !default;
|
||||
//** Danger progress bar color
|
||||
$progress-bar-danger-bg: $brand-danger !default;
|
||||
//** Info progress bar color
|
||||
$progress-bar-info-bg: $brand-info !default;
|
||||
|
||||
|
||||
//== List group
|
||||
//
|
||||
//##
|
||||
|
||||
//** Background color on `.list-group-item`
|
||||
$list-group-bg: #fff !default;
|
||||
//** `.list-group-item` border color
|
||||
$list-group-border: #ddd !default;
|
||||
//** List group border radius
|
||||
$list-group-border-radius: $border-radius-base !default;
|
||||
|
||||
//** Background color of single list items on hover
|
||||
$list-group-hover-bg: $bg-gray !default;
|
||||
//** Text color of active list items
|
||||
$list-group-active-color: $brand-info !default;
|
||||
//** Background color of active list items
|
||||
$list-group-active-bg: $component-active-bg !default;
|
||||
//** Border color of active list elements
|
||||
$list-group-active-border: $list-group-active-bg !default;
|
||||
//** Text color for content within active list items
|
||||
$list-group-active-text-color: lighten($list-group-active-bg, 40%) !default;
|
||||
|
||||
//** Text color of disabled list items
|
||||
$list-group-disabled-color: $gray-light !default;
|
||||
//** Background color of disabled list items
|
||||
$list-group-disabled-bg: $gray-lighter !default;
|
||||
//** Text color for content within disabled list items
|
||||
$list-group-disabled-text-color: $list-group-disabled-color !default;
|
||||
|
||||
$list-group-link-color: #555 !default;
|
||||
$list-group-link-hover-color: $list-group-link-color !default;
|
||||
$list-group-link-heading-color: #333 !default;
|
||||
|
||||
|
||||
//== Panels
|
||||
//
|
||||
//##
|
||||
|
||||
$panel-bg: #fff !default;
|
||||
$panel-body-padding: 15px !default;
|
||||
$panel-heading-padding: 18px 15px !default;
|
||||
$panel-footer-padding: $panel-heading-padding !default;
|
||||
$panel-border-radius: $border-radius-large !default;
|
||||
|
||||
// add sleede
|
||||
$panel-border: $border-color !default;
|
||||
$panel-heading-bg: #fff !default;
|
||||
$panel-footer-bg: #fff !default;
|
||||
|
||||
//** Border color for elements within panels
|
||||
$panel-inner-border: #ddd !default;
|
||||
$panel-footer-bg: $bg-gray !default;
|
||||
|
||||
$panel-default-text: $gray-dark !default;
|
||||
$panel-default-border: #ddd !default;
|
||||
$panel-default-heading-bg: $bg-gray !default;
|
||||
|
||||
$panel-primary-text: #fff !default;
|
||||
$panel-primary-border: #cbcbcb !default;
|
||||
$panel-primary-heading-bg: #fff !default;
|
||||
|
||||
$panel-success-text: $state-success-text !default;
|
||||
$panel-success-border: $state-success-border !default;
|
||||
$panel-success-heading-bg: $state-success-bg !default;
|
||||
|
||||
$panel-info-text: $state-info-text !default;
|
||||
$panel-info-border: $state-info-border !default;
|
||||
$panel-info-heading-bg: $state-info-bg !default;
|
||||
|
||||
$panel-warning-text: $state-warning-text !default;
|
||||
$panel-warning-border: $state-warning-border !default;
|
||||
$panel-warning-heading-bg: $state-warning-bg !default;
|
||||
|
||||
$panel-danger-text: $state-danger-text !default;
|
||||
$panel-danger-border: $state-danger-border !default;
|
||||
$panel-danger-heading-bg: $state-danger-bg !default;
|
||||
|
||||
|
||||
//== Thumbnails
|
||||
//
|
||||
//##
|
||||
|
||||
//** Padding around the thumbnail image
|
||||
$thumbnail-padding: 4px !default;
|
||||
//** Thumbnail background color
|
||||
$thumbnail-bg: $body-bg !default;
|
||||
//** Thumbnail border color
|
||||
$thumbnail-border: #ddd !default;
|
||||
//** Thumbnail border radius
|
||||
$thumbnail-border-radius: $border-radius-base !default;
|
||||
|
||||
//** Custom text color for thumbnail captions
|
||||
$thumbnail-caption-color: $text-color !default;
|
||||
//** Padding around the thumbnail caption
|
||||
$thumbnail-caption-padding: 9px !default;
|
||||
|
||||
|
||||
//== Wells
|
||||
//
|
||||
//##
|
||||
|
||||
$well-bg: #fff !default;
|
||||
$well-border: darken($well-bg, 7%) !default;
|
||||
|
||||
|
||||
//== Badges
|
||||
//
|
||||
//##
|
||||
|
||||
$badge-color: #fff !default;
|
||||
//** Linked badge text color on hover
|
||||
$badge-link-hover-color: #fff !default;
|
||||
$badge-bg: $gray-light !default;
|
||||
|
||||
//** Badge text color in active nav link
|
||||
$badge-active-color: $link-color !default;
|
||||
//** Badge background color in active nav link
|
||||
$badge-active-bg: #fff !default;
|
||||
|
||||
$badge-font-weight: bold !default;
|
||||
$badge-line-height: 1 !default;
|
||||
$badge-border-radius: 10px !default;
|
||||
|
||||
|
||||
//== Breadcrumbs
|
||||
//
|
||||
//##
|
||||
|
||||
$breadcrumb-padding-vertical: 8px !default;
|
||||
$breadcrumb-padding-horizontal: 15px !default;
|
||||
//** Breadcrumb background color
|
||||
$breadcrumb-bg: $bg-gray !default;
|
||||
//** Breadcrumb text color
|
||||
$breadcrumb-color: #ccc !default;
|
||||
//** Text color of current page in the breadcrumb
|
||||
$breadcrumb-active-color: $gray-light !default;
|
||||
//** Textual separator for between breadcrumb elements
|
||||
$breadcrumb-separator: "/" !default;
|
||||
|
||||
|
||||
//== Carousel
|
||||
//
|
||||
//##
|
||||
|
||||
$carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6) !default;
|
||||
|
||||
$carousel-control-color: #fff !default;
|
||||
$carousel-control-width: 15% !default;
|
||||
$carousel-control-opacity: .5 !default;
|
||||
$carousel-control-font-size: 20px !default;
|
||||
|
||||
$carousel-indicator-active-bg: #fff !default;
|
||||
$carousel-indicator-border-color: #fff !default;
|
||||
|
||||
$carousel-caption-color: #fff !default;
|
||||
|
||||
|
||||
//== Close
|
||||
//
|
||||
//##
|
||||
|
||||
$close-font-weight: bold !default;
|
||||
$close-color: #000 !default;
|
||||
$close-text-shadow: 0 1px 0 #fff !default;
|
||||
|
||||
|
||||
//== Code
|
||||
//
|
||||
//##
|
||||
|
||||
$code-color: #c7254e !default;
|
||||
$code-bg: #f9f2f4 !default;
|
||||
|
||||
$kbd-color: #fff !default;
|
||||
$kbd-bg: #333 !default;
|
||||
|
||||
$pre-bg: transparent !default;
|
||||
$pre-color: $gray-dark !default;
|
||||
$pre-border-color: transparent !default;
|
||||
$pre-scrollable-max-height: 340px !default;
|
||||
|
||||
|
||||
//== Type
|
||||
//
|
||||
//##
|
||||
//** Horizontal offset for forms and lists.
|
||||
$component-offset-horizontal: 180px !default;
|
||||
//** Text muted color
|
||||
$text-muted: $gray-light !default;
|
||||
//** Abbreviations and acronyms border color
|
||||
$abbr-border-color: $gray-light !default;
|
||||
//** Headings small color
|
||||
$headings-small-color: $gray-light !default;
|
||||
//** Blockquote small color
|
||||
$blockquote-small-color: $gray-light !default;
|
||||
//** Blockquote font size
|
||||
$blockquote-font-size: ($font-size-base * 1.25) !default;
|
||||
//** Blockquote border color
|
||||
$blockquote-border-color: $gray-lighter !default;
|
||||
//** Page header border color
|
||||
$page-header-border-color: $gray-lighter !default;
|
||||
//** Width of horizontal description list titles
|
||||
$dl-horizontal-offset: $component-offset-horizontal !default;
|
||||
//** Horizontal line color.
|
||||
$hr-border: $gray-lighter !default;
|
||||
|
||||
|
||||
// Core variables and mixins
|
||||
// @import "bootstrap/variables"; //overrides
|
||||
@import "bootstrap/mixins";
|
||||
|
||||
// Reset and dependencies
|
||||
@import "bootstrap/normalize";
|
||||
@import "bootstrap/print";
|
||||
@import "bootstrap/glyphicons";
|
||||
|
||||
// Core CSS
|
||||
@import "bootstrap/scaffolding";
|
||||
@import "bootstrap/type";
|
||||
@import "bootstrap/code";
|
||||
@import "bootstrap/grid";
|
||||
@import "bootstrap/tables";
|
||||
@import "bootstrap/forms";
|
||||
@import "bootstrap/buttons";
|
||||
|
||||
// Components
|
||||
@import "bootstrap/component-animations";
|
||||
@import "bootstrap/dropdowns";
|
||||
@import "bootstrap/button-groups";
|
||||
@import "bootstrap/input-groups";
|
||||
@import "bootstrap/navs";
|
||||
@import "bootstrap/navbar";
|
||||
// @import "bootstrap/breadcrumbs";
|
||||
@import "bootstrap/pagination";
|
||||
// @import "bootstrap/pager";
|
||||
@import "bootstrap/labels";
|
||||
@import "bootstrap/badges";
|
||||
//@import "bootstrap/jumbotron";
|
||||
@import "bootstrap/thumbnails";
|
||||
@import "bootstrap/alerts";
|
||||
@import "bootstrap/progress-bars";
|
||||
// @import "bootstrap/media";
|
||||
@import "bootstrap/list-group";
|
||||
@import "bootstrap/panels";
|
||||
@import "bootstrap/responsive-embed";
|
||||
@import "bootstrap/wells";
|
||||
@import "bootstrap/close";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "bootstrap/modals";
|
||||
@import "bootstrap/tooltip";
|
||||
@import "bootstrap/popovers";
|
||||
@import "bootstrap/carousel";
|
||||
|
||||
// Utility classes
|
||||
@import "bootstrap/utilities";
|
||||
@import "bootstrap/responsive-utilities";
|
80
app/assets/templates/admin/events/index.html.erb
Normal file
@ -0,0 +1,80 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter b-b">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>Les Stages et ateliers du Fab Lab</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin'])">
|
||||
<section class="heading-actions wrapper">
|
||||
<a class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.events_new" role="button">Ajouter un évènement</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="m-lg">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="col-md-6 m-b">
|
||||
<select ng-model="selectedTimezone" class="form-control">
|
||||
<option value="">Tous les évènements</option>
|
||||
<option value="passed">Les évènements déjà passés</option>
|
||||
<option value="future">Les évènements à venir</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:30%">Titre</th>
|
||||
<th style="width:30%">Dates</th>
|
||||
<th style="width:40%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="event in filtered = (events | eventsFilter:selectedTimezone)">
|
||||
<td>
|
||||
<a ui-sref="app.public.events_show({id: event.id})">{{ event.title }} </a>
|
||||
</td>
|
||||
<td>
|
||||
<span>Du {{event.start_date | amDateFormat:'DD/MM/YYYY'}}<span class="text-sm font-thin"> au </span>{{event.end_date | amDateFormat:'DD/MM/YYYY'}}</span>
|
||||
<br/>
|
||||
<span ng-if="event.all_day == 'true'">Toute la journée</span>
|
||||
<span ng-if="event.all_day == 'false'">
|
||||
De {{event.start_date | date:'HH:mm'}}
|
||||
<span class="text-sm font-thin"> à </span>
|
||||
{{event.end_date | date:'HH:mm'}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<button class="btn btn-default" ui-sref="app.admin.events_edit({id: event.id})">
|
||||
<i class="fa fa-edit"></i> Éditer
|
||||
</button>
|
||||
<%#<button class="btn" ng-click="removeEvent(event)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>%>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<a class="btn btn-warning" ng-click="loadMoreEvents()" ng-if="paginateActive">Charger les stages et ateliers suivants ...</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
9
app/assets/templates/admin/members/_form.html.erb
Normal file
@ -0,0 +1,9 @@
|
||||
<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">Type d'utilisateur</label>
|
||||
<div class="col-sm-9">
|
||||
<select ng-model="user.group_id" class="form-control" name="user[group_id]" id="user_group_id" ng-options="g.id as g.name for g in groups" required>
|
||||
</select>
|
||||
<input type="hidden" name="user[group_id]" ng-value="user.group_id" />
|
||||
<span class="help-block" ng-show="userForm['user[group_id]'].$dirty && userForm['user[group_id]'].$error.required">Le type d'utilisateur est obligatoire</span>
|
||||
</div>
|
||||
</div>
|
53
app/assets/templates/admin/members/edit.html.erb
Normal file
@ -0,0 +1,53 @@
|
||||
<div>
|
||||
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-md-1 hidden-xs">
|
||||
<section class="heading-btn">
|
||||
<a ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-8 b-l b-r">
|
||||
<section class="heading-title">
|
||||
<h1>Utilisateur : {{ user.name }}</h1>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<section class="heading-actions wrapper">
|
||||
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()">
|
||||
Annuler
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter ">
|
||||
<div class="col-sm-12 col-md-12 b-r">
|
||||
|
||||
<form role="form" name="userForm" class="form-horizontal col-md-8" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
|
||||
|
||||
<section class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
<ng-include src="'<%= asset_path 'shared/_member_form.html' %>'"></ng-include>
|
||||
|
||||
<ng-include src="'<%= asset_path 'admin/members/_form.html' %>'"></ng-include>
|
||||
|
||||
</div> <!-- ./panel-body -->
|
||||
|
||||
|
||||
<div class="panel-footer no-padder">
|
||||
<input type="submit" value="Valider les modifications" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
71
app/assets/templates/admin/members/index.html.erb
Normal file
@ -0,0 +1,71 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||
<section class="heading-title">
|
||||
<h1>Liste des membres</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="m-lg">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
|
||||
<input type="text" ng-model="searchMember" class="form-control" placeholder="Recherchez un utilisateur">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.members_new">Ajouter un nouveau membre</button>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-default" ng-href="api/members/export_members.xls" target="_blank">
|
||||
<i class="fa fa-file-excel-o"></i> Membres
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderMember('last_name')">Nom <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderMember=='last_name', 'fa fa-sort-alpha-desc': orderMember=='-last_name', 'fa fa-arrows-v': orderMember }"></i></a></th>
|
||||
|
||||
<th style="width:15%"><a href="" ng-click="setOrderMember('first_name')">Prénom <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderMember=='first_name', 'fa fa-sort-alpha-desc': orderMember=='-first_name', 'fa fa-arrows-v': orderMember }"></i></a></th>
|
||||
|
||||
<th style="width:15%"><a href="" ng-click="setOrderMember('email')">Email <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderMember=='email', 'fa fa-sort-alpha-desc': orderMember=='-email', 'fa fa-arrows-v': orderMember }"></i></a></th>
|
||||
|
||||
<th style="width:10%"><a href="" ng-click="setOrderMember('profile.phone')">Tel. <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderMember=='profile.phone', 'fa fa-sort-numeric-desc': orderMember=='-profile.phone', 'fa fa-arrows-v': orderMember }"></i></a></th>
|
||||
|
||||
<th style="width:20%"><a href="" ng-click="setOrderMember('group.name')">Type utilisateur <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderMember=='group.name', 'fa fa-sort-alpha-desc': orderMember=='-group.name', 'fa fa-arrows-v': orderMember }"></i></a></th>
|
||||
|
||||
<th style="width:10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="member in members | filter:searchMember | orderBy:orderMember">
|
||||
<td class="text-c">{{ member.profile.last_name }}</td>
|
||||
<td class="text-c">{{ member.profile.first_name }}</td>
|
||||
<td>{{ member.email }}</td>
|
||||
<td>{{ member.profile.phone }}</td>
|
||||
<td class="text-u-c text-sm">{{ member.group.name }}</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<button class="btn btn-default" ui-sref="app.admin.members_edit({id: member.id})">
|
||||
<i class="fa fa-edit"></i> Éditer
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
52
app/assets/templates/admin/members/new.html.erb
Normal file
@ -0,0 +1,52 @@
|
||||
<div>
|
||||
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-md-1 hidden-xs">
|
||||
<section class="heading-btn">
|
||||
<a ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-8 b-l b-r">
|
||||
<section class="heading-title">
|
||||
<h1>Ajouter un membre</h1>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<section class="heading-actions wrapper">
|
||||
<div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()">
|
||||
Annuler
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class=" col-sm-12 col-md-9 b-r nopadding">
|
||||
|
||||
<form role="form" name="userForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
|
||||
|
||||
<section class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
<ng-include src="'<%= asset_path 'shared/_member_form.html' %>'"></ng-include>
|
||||
|
||||
<ng-include src="'<%= asset_path 'admin/members/_form.html' %>'"></ng-include>
|
||||
</div> <!-- ./panel-body -->
|
||||
|
||||
|
||||
<div class="panel-footer no-padder">
|
||||
<input type="submit" value="Enregistrer" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
153
app/assets/templates/admin/project_elements/index.html.erb
Normal file
@ -0,0 +1,153 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||
<section class="heading-title">
|
||||
<h1>Gestion des éléments projets</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="m-lg">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
<tabset justified="true">
|
||||
<tab heading="Matériaux">
|
||||
<button type="button" class="btn btn-warning m-b m-t" ng-click="addComponent()">Ajouter un matériau</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:80%">Nom</th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="component in components">
|
||||
<td>
|
||||
<span editable-text="component.name" e-cols="100" e-name="name" e-form="rowform" e-required>
|
||||
{{ component.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<!-- form -->
|
||||
<form editable-form name="rowform" onbeforesave="saveComponent($data, component.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == component">
|
||||
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-warning">
|
||||
<i class="fa fa-check"></i>
|
||||
</button>
|
||||
<button type="button" ng-disabled="rowform.$waiting" ng-click="cancelComponent(rowform, $index)" class="btn btn-default">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
<div class="buttons" ng-show="!rowform.$visible">
|
||||
<button class="btn btn-default" ng-click="rowform.$show()">
|
||||
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm">Éditer</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="removeComponent($index)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tab>
|
||||
<tab heading="Thématiques">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ng-click="addTheme()">Ajouter une nouvelle thématique</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:80%">Nom</th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="theme in themes">
|
||||
<td>
|
||||
<span editable-text="theme.name" e-name="name" e-form="rowform" e-required>
|
||||
{{ theme.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<!-- form -->
|
||||
<form editable-form name="rowform" onbeforesave="saveTheme($data, theme.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == theme">
|
||||
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-warning">
|
||||
<i class="fa fa-check"></i>
|
||||
</button>
|
||||
<button type="button" ng-disabled="rowform.$waiting" ng-click="cancelTheme(rowform, $index)" class="btn btn-default">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
<div class="buttons" ng-show="!rowform.$visible">
|
||||
<button class="btn btn-default" ng-click="rowform.$show()">
|
||||
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm">Éditer</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="removeTheme($index)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tab>
|
||||
<tab heading="Licences">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ng-click="addLicence()">Ajouter une nouvelle licence</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:30%">Nom</th>
|
||||
<th style="width:50%" class="hidden-xs">Description</th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="licence in licences">
|
||||
<td>
|
||||
<span editable-textarea="licence.name" e-rows="5" e-cols="100" e-name="name" e-form="rowform" e-required>
|
||||
{{ licence.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
<span editable-textarea="licence.description" e-rows="5" e-cols="100" e-name="description" e-form="rowform" e-required>
|
||||
<div class="text-sm">{{ licence.description }}</div>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<!-- form -->
|
||||
<form editable-form name="rowform" onbeforesave="saveLicence($data, licence.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == licence">
|
||||
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-warning">
|
||||
<i class="fa fa-check"></i>
|
||||
</button>
|
||||
<button type="button" ng-disabled="rowform.$waiting" ng-click="cancelLicence(rowform, $index)" class="btn btn-default">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
<div class="buttons" ng-show="!rowform.$visible">
|
||||
<button class="btn btn-default" ng-click="rowform.$show()">
|
||||
<i class="fa fa-edit"></i> <span class="hidden-xs hidden-sm">Éditer</span>
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="removeLicence($index)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tab>
|
||||
</tabset>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
22
app/assets/templates/dashboard/nav.html.erb
Normal file
@ -0,0 +1,22 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-10 b-l">
|
||||
<section class="heading-title m-l">
|
||||
<h4 class="m-l text-sm">Tableau de bord</h4>
|
||||
<ul class="nav-page nav nav-pills text-u-c text-sm">
|
||||
<li ui-sref-active="active"><a class="text-black" href="#" ui-sref="app.logged.dashboard_profile">Mon profil</a></li>
|
||||
<li ui-sref-active="active"><a class="text-black" href="#" ui-sref="app.logged.dashboard_projects">Mes projets</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
56
app/assets/templates/dashboard/profile.html.erb
Normal file
@ -0,0 +1,56 @@
|
||||
<div>
|
||||
|
||||
<ng-include src="'<%= asset_path 'dashboard/nav.html' %>'"></ng-include>
|
||||
|
||||
|
||||
|
||||
<div class="row no-gutter wrapper">
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small text-center">
|
||||
<span class="avatar ">
|
||||
<fab-user-avatar ng-model="user.profile.user_avatar" avatar-class="thumb-50">test</fab-user-avatar>
|
||||
</span>
|
||||
<div class="font-sbold m-t-sm">{{user.name}}</div>
|
||||
<div>{{user.email}}</div>
|
||||
<div class="text-xs" ng-if="user.last_sign_in_at"><i>Dernière activité le {{user.last_sign_in_at | amDateFormat: 'Do MMMM '}}</i></div>
|
||||
</div>
|
||||
<div class="widget-content no-bg auto wrapper">
|
||||
|
||||
<div class="m-t">
|
||||
<h3 class="text-u-c">Projets</h3>
|
||||
<ul class="list-unstyled" ng-if="user.all_projects.length > 0">
|
||||
<li ng-repeat="p in user.all_projects">
|
||||
{{p.name}}
|
||||
</li>
|
||||
</ul>
|
||||
<div ng-if="user.all_projects.length == 0">Aucun projet</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-9">
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b">
|
||||
<h1 class="red text-u-c">Éditer votre profil</h1>
|
||||
</div>
|
||||
<form role="form" name="userForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
|
||||
<div class="widget-content no-bg auto">
|
||||
<section class="panel panel-default bg-light m">
|
||||
<div class="panel-body m-r">
|
||||
<ng-include src="'<%= asset_path 'shared/_member_form.html' %>'"></ng-include>
|
||||
</div> <!-- ./panel-body -->
|
||||
</section>
|
||||
</div>
|
||||
<div class="panel-footer no-padder">
|
||||
<input type="submit" value="Valider les modifications" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="userForm.$invalid"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
80
app/assets/templates/dashboard/projects.html.erb
Normal file
@ -0,0 +1,80 @@
|
||||
<div>
|
||||
|
||||
<section class="heading">
|
||||
<div class="row no-gutter">
|
||||
<ng-include src="'<%= asset_path 'dashboard/nav.html' %>'"></ng-include>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
|
||||
<div class="wrapper" ng-if="user.all_projects.length == 0">Vous n'avez aucun projet.</div>
|
||||
<div class="widget panel b-a m m-t-lg" ng-repeat="project in user.all_projects">
|
||||
<div class="panel-heading b-b clearfix">
|
||||
<h4 class="text-u-c font-sbold pull-left">{{project.name}}</h4> <span class="m-l-sm label label-success text-white">{{project.author_id == currentUser.id ? 'Auteur' : 'Collaborateur'}}</span>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-warning bg-white b-2x rounded upper text-sm text-black" ui-sref="app.public.projects_show({id:project.slug})" role="button">Consulter</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-content bg-light clearfix">
|
||||
|
||||
<div class="col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="widget panel b-a r-n m-t-md">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Description</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper text-black-light">
|
||||
<!-- TODO -->
|
||||
<!-- <div class="text-sm font-sbold "><i>Par {{project.author.first_name}}</i></div>
|
||||
<small class="text-xs m-b"><i>posté le {{project.created_at | amDateFormat: 'Do MMMM YYYY'}}</i></small> -->
|
||||
<div ng-bind-html="project.description | humanize : 180 | toTrusted"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="widget panel b-a r-n m-t-md">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Machines et matériaux</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<h3 class="text-black-light font-sbold"><i class="fa fa-rocket red"></i> Machines :</h3>
|
||||
<ul class="list-unstyled m-l-md text-black-light">
|
||||
<li ng-repeat="m in project.machines">{{m.name}}</li>
|
||||
</ul>
|
||||
<h3 class="text-black-light font-sbold"><i class="fa fa-cog red"></i> Matériaux :</h3>
|
||||
<ul class="list-unstyled m-l-md text-black-light">
|
||||
<li ng-repeat="c in project.components">{{c.name}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-4 col-lg-4">
|
||||
<div class="widget panel b-a r-n m-t-md">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Les collaborateurs</h3>
|
||||
</div>
|
||||
<div class="widget-content list-group-lg no-bg auto wrapper">
|
||||
<li class="list-group-item no-b clearfix" ng-repeat="collaborator in project.project_users">
|
||||
<span class="pull-left thumb-sm avatar m-r-lg">
|
||||
<fab-user-avatar ng-model="collaborator.user_avatar" avatar-class="thumb-50"></fab-user-avatar>
|
||||
|
||||
<i class="on b-white bottom" ng-if="collaborator.is_valid"></i>
|
||||
<i class="off b-white bottom" ng-if="!collaborator.is_valid"></i>
|
||||
</span>
|
||||
<span class="clear"><span>{{collaborator.full_name}}</span>
|
||||
<small class="text-muted clear text-ellipsis text-c">{{collaborator.username}}</small>
|
||||
</span>
|
||||
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
216
app/assets/templates/events/_form.html.erb
Normal file
@ -0,0 +1,216 @@
|
||||
<div class="row no-gutter">
|
||||
|
||||
<div class=" col-sm-12 col-md-12 col-lg-8 nopadding">
|
||||
|
||||
<section class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
|
||||
<alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</alert>
|
||||
|
||||
<input name="_method" type="hidden" ng-value="method">
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': eventForm['event[title]'].$dirty && eventForm['event[title]'].$invalid}">
|
||||
<label for="event_title" class="col-sm-3 control-label">Titre *</label>
|
||||
<div class="col-sm-9">
|
||||
<input ng-model="event.title" type="text" name="event[title]" class="form-control" id="event_title" placeholder="" required>
|
||||
<span class="help-block" ng-show="eventForm['event[title]'].$dirty && eventForm['event[title]'].$error.required">Titre est obligatoire</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="event_image" class="col-sm-3 control-label">Visuel associé</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="fileinput" data-provides="fileinput" ng-class="fileinputClass(event.event_image)">
|
||||
<div class="fileinput-new thumbnail" style="width: 334px; height: 250px;">
|
||||
<img src="data:image/png;base64," src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!event.event_image">
|
||||
</div>
|
||||
<div class="fileinput-preview fileinput-exists thumbnail" data-trigger="fileinput" style="max-width: 334px;">
|
||||
<img ng-src="{{ event.event_image }}" alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="btn btn-default btn-file"><span class="fileinput-new">Choisir une image <i class="fa fa-upload fa-fw"></i></span><span class="fileinput-exists">Modifier</span>
|
||||
<input type="file" name="event[event_image_attributes][attachment]"></span>
|
||||
<a class="btn btn-danger fileinput-exists" data-dismiss="fileinput"><i class="fa fa-trash-o"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': eventForm['event[description]'].$dirty && eventForm['event[description]'].$invalid}">
|
||||
<label for="description" class="col-sm-3 control-label">Description *</label>
|
||||
<div class="col-sm-9">
|
||||
<textarea ng-model="event.description" rows="16" class="form-control" id="event_description" placeholder="" name="event[description]" required></textarea>
|
||||
<span class="help-block" ng-show="eventForm['event[description]'].$dirty && eventForm['event[description]'].$error.required">Description est obligatoire</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Pièces jointes</label>
|
||||
<div class="col-sm-9">
|
||||
<div ng-repeat="file in event.event_files_attributes" ng-show="!file._destroy">
|
||||
<input type="hidden" name="event[event_files_attributes][][id]" ng-value="file.id" />
|
||||
<input type="hidden" name="event[event_files_attributes][][_destroy]" ng-value="file._destroy" />
|
||||
|
||||
<div class="fileinput input-group" data-provides="fileinput" ng-class="fileinputClass(file.attachment)">
|
||||
<div class="form-control" data-trigger="fileinput">
|
||||
<i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">{{file.attachment}}</span>
|
||||
</div>
|
||||
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new">Parcourir</span>
|
||||
<span class="fileinput-exists">Modifier</span><input type="file" name="event[event_files_attributes][][attachment]"></span>
|
||||
<a class="input-group-addon btn btn-danger fileinput-exists" data-dismiss="fileinput" ng-click="deleteFile(file)"><i class="fa fa-trash-o"></i></a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a class="btn btn-default" ng-click="addFile()" role="button">Ajouter un nouveau fichier <i class="fa fa-file-o fa-fw"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div> <!-- ./panel-body -->
|
||||
<div class="panel-footer no-padder">
|
||||
<input type="submit" ng-value="submitName" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="eventForm.$invalid"/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-4">
|
||||
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Type d'évènement</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<input type="hidden" name="event[category_ids][]" value="" />
|
||||
<select ng-model="event.category_ids" class="form-control" name="event[category_ids][]" required ui-select2>
|
||||
<option value="{{c.id}}" ng-repeat="c in categories">{{c.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Dates et horaires</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<div class="m-b">
|
||||
<label class="v-bottom">Toute la journée</label>
|
||||
<div class="inline v-top">
|
||||
<label class="checkbox-inline">
|
||||
<input type="radio" name="event[all_day]" ng-model="event.all_day" value="true" required/> Oui
|
||||
</label>
|
||||
<label class="checkbox-inline">
|
||||
<input type="radio" name="event[all_day]" ng-model="event.all_day" value="false"/> Non
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="event[availability_id]" ng-value="event.availability_id" ng-if="event.availability_id">
|
||||
<div class="m-b">
|
||||
<label>Date de début</label>
|
||||
<div class="input-group">
|
||||
<input type="hidden" name="event[start_date]" ng-value="event.start_date">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="event.start_date"
|
||||
datepicker-popup="dd/MM/yyyy"
|
||||
datepicker-options="datePicker.options"
|
||||
is-open="datePicker.startOpened"
|
||||
ng-click="toggleStartDatePicker($event)"
|
||||
required/>
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" ng-click="toggleStartDatePicker($event)"><i class="fa fa-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-b">
|
||||
<label>Date de fin</label>
|
||||
<div class="input-group">
|
||||
<input type="hidden" name="event[end_date]" ng-value="event.end_date">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="event.end_date"
|
||||
datepicker-popup="dd/MM/yyyy"
|
||||
datepicker-options="datePicker.options"
|
||||
is-open="datePicker.endOpened"
|
||||
ng-click="toggleEndDatePicker($event)"
|
||||
required/>
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" ng-click="toggleEndDatePicker($event)"><i class="fa fa-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-b row" ng-if="event.all_day =='false'">
|
||||
<div class="col-xs-6">
|
||||
<label>Heure de début</label>
|
||||
<div>
|
||||
<input type="hidden" name="event[start_time]" ng-value="event.start_time">
|
||||
<timepicker ng-model="event.start_time" hour-step="1" minute-step="1" show-meridian="ismeridian"></timepicker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label>Heure de fin</label>
|
||||
<div>
|
||||
<input type="hidden" name="event[end_time]" ng-value="event.end_time">
|
||||
<timepicker ng-model="event.end_time" hour-step="1" minute-step="1" show-meridian="ismeridian"></timepicker>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="method == 'post'" class="m-b">
|
||||
<label>Récurrence</label>
|
||||
<select ng-model="event.recurrence" class="form-control" name="event[recurrence]">
|
||||
<option value="{{t.value}}" ng-repeat="t in recurrenceTypes">{{t.label}}</option>
|
||||
</select>
|
||||
<div ng-if="event.recurrence != 'none'">
|
||||
et se terminera le
|
||||
<div class="input-group">
|
||||
<input type="hidden" name="event[recurrence_end_at]" ng-value="event.recurrence_end_at">
|
||||
<input type="text" class="form-control" datepicker-popup="dd/MM/yyyy" ng-model="event.recurrence_end_at" is-open="datePicker.recurrenceEndOpened" ng-required="true"/>
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" ng-click="toggleRecurrenceEnd($event)"><i class="fa fa-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Tarifs et disponibilités</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<div class="form-group">
|
||||
<label for="event_amount" class="col-sm-5 control-label">Tarif standard</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<input ng-model="event.amount" type="number" name="event[amount]" class="form-control" id="event_amount" required>
|
||||
<div class="input-group-addon">€</div>
|
||||
</div>
|
||||
<span class="help-block">0 = gratuit</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="event_reduced_amount" class="col-sm-5 control-label">Tarif réduit</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<input ng-model="event.reduced_amount" type="number" name="event[reduced_amount]" class="form-control" id="event_reduced_amount">
|
||||
<div class="input-group-addon">€</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="event_nb_total_places" class="col-sm-5 control-label">Places disponibles</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<input ng-model="event.nb_total_places" type="number" name="event[nb_total_places]" class="form-control" id="event_nb_total_places">
|
||||
<div class="input-group-addon"><i class="fa fa-ticket"></i> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
25
app/assets/templates/events/edit.html.erb
Normal file
@ -0,0 +1,25 @@
|
||||
<div>
|
||||
|
||||
<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 ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l ">
|
||||
<section class="heading-title">
|
||||
<h1>Editer l'évènement</h1>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<form role="form" name="eventForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
|
||||
|
||||
<ng-include src="'<%= asset_path 'events/_form.html' %>'"></ng-include>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
61
app/assets/templates/events/index.html.erb
Normal file
@ -0,0 +1,61 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter b-b">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>Les Stages et ateliers du Fab Lab</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin'])">
|
||||
<section class="heading-actions wrapper">
|
||||
<a class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.events_new" role="button">Ajouter un évènement</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="m-lg">
|
||||
|
||||
<div ng-repeat="month in monthOrder">
|
||||
<h1>{{month.split(',')[0]}}, {{month.split(',')[1]}}</h1>
|
||||
|
||||
<div class="row" ng-repeat="event in (eventsGroupByMonth[month].length/3 | array)">
|
||||
|
||||
<div class="col-xs-12 col-sm-6 col-md-4" ng-repeat="event in eventsGroupByMonth[month].slice(3*$index, 3*$index + 3)" ng-click="showEvent(event)">
|
||||
|
||||
|
||||
<a class="block bg-white img-full p-sm p-l-m box-h-m event b b-light-dark" ui-sref="app.public.events_show({id: event.id})">
|
||||
<div class="pull-left half-w m-t-n-sm">
|
||||
<h5 class="text-xs">{{event.categories[0].name}}</h5>
|
||||
<h4 class="m-n text-sm clear l-n">{{event.title}}</h4>
|
||||
<h3 class="m-n">{{event.start_date | amDateFormat:'DD/MM'}}<span class="text-sm font-thin"> au </span>{{event.end_date | amDateFormat:'DD/MM'}}</h3>
|
||||
|
||||
<h6 class="m-n" ng-if="event.amount">Plein tarif: {{event.amount}}€ <span ng-if="event.reduced_amount > 0">/ Tarif réduit: {{event.reduced_amount}}€</span></h6>
|
||||
</div>
|
||||
<!-- Event Image -->
|
||||
<div class="pull-right crop-130">
|
||||
<img class="pull-right img-responsive" ng-src="{{event.event_image}}" title="{{event.title}}" ng-if="event.event_image">
|
||||
<img class="pull-right img-responsive" src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!event.event_image">
|
||||
</div>
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12 text-center m-t-md">
|
||||
<a class="btn btn-warning" ng-click="loadMoreEvents()" ng-if="paginateActive">Charger les stages et ateliers suivants ...</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
25
app/assets/templates/events/new.html.erb
Normal file
@ -0,0 +1,25 @@
|
||||
<div>
|
||||
|
||||
<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 ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l ">
|
||||
<section class="heading-title">
|
||||
<h1>Ajouter un évènement</h1>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<form role="form" name="eventForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
|
||||
|
||||
<ng-include src="'<%= asset_path 'events/_form.html' %>'"></ng-include>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
100
app/assets/templates/events/show.html.erb
Normal file
@ -0,0 +1,100 @@
|
||||
<div>
|
||||
<ui-view autoscroll='true'></ui-view>
|
||||
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ event.title }} <span class="v-middle badge text-xs bg-formation">{{event.categories[0].name}}</span></h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized('admin')">
|
||||
<section class="heading-actions wrapper">
|
||||
|
||||
<a ui-sref="app.admin.events_edit({id: event.id})" ng-if="isAuthorized('admin')" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs text-u-c text-sm"><i class="fa fa-edit"></i> éditer</a>
|
||||
<a ng-click="deleteEvent(event)" ng-if="isAuthorized('admin')" class="btn btn-lg btn-danger b-2x rounded no-b m-t-xs"><i class="fa fa-trash-o"></i></a>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-12 col-lg-8">
|
||||
|
||||
<div class="article wrapper-lg">
|
||||
|
||||
<div class="article-thumbnail" ng-if="event.event_image">
|
||||
<img ng-src="{{event.event_image}}" alt="{{event.title}}" class="img-responsive">
|
||||
</div>
|
||||
|
||||
<h3>Description de l'évènement</h3>
|
||||
<p ng-bind-html="event.description | breakFilter"></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-4">
|
||||
|
||||
<section class="widget panel b-a m" ng-if="event.event_files_attributes">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{event.event_files_attributes.length}}</span>
|
||||
<h3>Documents à télécharger</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="file in event.event_files_attributes" class="list-group-item no-b clearfix">
|
||||
<a target="_blank" ng-href="{{file.attachment_url}}"><i class="fa fa-arrow-circle-o-down"> </i> {{file.attachment | humanize : 25}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Informations</h3>
|
||||
</div>
|
||||
|
||||
<div class="panel-content wrapper">
|
||||
|
||||
<h5>{{event.categories[0].name}}</h5>
|
||||
<dl class="text-sm">
|
||||
<dt><i class="fa fa-calendar"></i> Dates :</dt>
|
||||
<dd>Début: <span class="text-u-l">{{event.start_date | amDateFormat:'DD/MM/YYYY'}}</span><br>Fin: <span class="text-u-l">{{event.end_date | amDateFormat:'DD/MM/YYYY'}}</span></dd>
|
||||
<dt><i class="fa fa-clock-o"></i> Horaires :</dt>
|
||||
<dd ng-if="event.all_day == 'true'"><span>Toute la journée</span></dd>
|
||||
<dd ng-if="event.all_day == 'false'">De <span class="text-u-l">{{event.start_date | amDateFormat:'HH:mm'}}</span> à <span class="text-u-l">{{event.end_date | amDateFormat:'HH:mm'}}</span></dd>
|
||||
</dl>
|
||||
|
||||
<div class="text-sm" ng-if="event.amount">
|
||||
<div>Plein tarif : <span>{{ event.amount }} €</span></div>
|
||||
<div ng-if="event.reduced_amount > 0">Tarif réduit* : {{ event.reduced_amount }} €</div>
|
||||
</div>
|
||||
|
||||
<div class="text-sm m-b" ng-if="event.nb_total_places">
|
||||
<div>Places disponibles: <span class="font-sbold">{{event.nb_total_places}}</span></div>
|
||||
</div>
|
||||
<div class="text-sm m-b" ng-if="!event.nb_total_places">
|
||||
<div><span class="badge font-sbold">Entrée libre</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<div ng-if="event.reduced_amount" class="alert alert-warning text-sm m" role="alert">
|
||||
* Tarif réduit si vous avez moins de 25 ans, que vous êtes étudiant ou demandeur d'emploi.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
126
app/assets/templates/home.html.erb
Normal file
@ -0,0 +1,126 @@
|
||||
<div>
|
||||
|
||||
<div class="row wrapper">
|
||||
<div class="col-lg-8">
|
||||
<h4 class="text-sm m-t-sm">Les derniers projets documentés</h4>
|
||||
|
||||
<carousel interval="5000" disable-animation="true">
|
||||
<slide class="h480 cover r" ng-repeat="p in last_projects" active="p.active" style="background-image:url({{p.project_image}});">
|
||||
<div class="carousel-caption">
|
||||
<h1 class="title"><a ui-sref="app.public.projects_show({id:p.slug})">{{p.name}}</a></h1>
|
||||
</div>
|
||||
</slide>
|
||||
</carousel>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-lg-4 m-t-lg">
|
||||
|
||||
<section class="widget panel b-a m-t-sm" ng-if="last_tweets">
|
||||
<div class="panel-heading b-b small">
|
||||
<div class="pull-right text-xs align">
|
||||
<a href="https://twitter.com/<%= ENV['TWITTER_NAME'] %>" target="_blank">Suivez-nous
|
||||
<span class="fa-stack fa-lg">
|
||||
<i class="fa fa-circle fa-stack-2x text-yellow"></i>
|
||||
<i class="fa fa-twitter fa-stack-1x fa-inverse text-white"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<h2>Les derniers tweets</h2>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="tweet in last_tweets" class="text-sm list-group-item no-b clearfix" ng-bind-html="tweet.text">
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a" >
|
||||
<div class="panel-heading small b-b">
|
||||
<h2>Derniers membres inscrits</h2>
|
||||
</div>
|
||||
|
||||
<div class="row m-n">
|
||||
<div class="col-md-6 b-b b-r block-link" ng-repeat="member in last_members" ui-sref="app.logged.members_show({id:member.slug})">
|
||||
|
||||
<div class="padder-v">
|
||||
<span class="avatar avatar-block text-center">
|
||||
<fab-user-avatar ng-model="member.profile.user_avatar" avatar-class="thumb-50"></fab-user-avatar>
|
||||
<a ><span class="user-name m-l-sm text-black m-t-xs">{{member.name}}</span></a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="m-t-sm m-b-sm text-center" ng-if="!isAuthenticated()">
|
||||
<button href="#" ng-click="signup($event)" class="btn btn-warning-full width-70 font-sbold rounded text-sm">Créer un compte</button>
|
||||
</div>
|
||||
|
||||
<div class="m-t-sm m-b-sm text-center" ng-if="isAuthenticated()">
|
||||
<button href="#" ui-sref="app.logged.members" class="btn btn-warning-full width-70 font-sbold rounded text-sm">Découvrir les membres</button>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<section class="col-lg-12 wrapper">
|
||||
<h4 class="text-sm m-t-sm">Les prochains ateliers et stages du fablab <a ui-sref="app.public.events_list" class="pull-right"><i class="fa fa-tags"></i> Tous les événements</a></h4>
|
||||
|
||||
<div class="row" ng-repeat="event in (upcoming_events.length/3 | array)">
|
||||
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-4" ng-repeat="event in upcoming_events.slice(3*$index, 3*$index + 3)">
|
||||
|
||||
|
||||
<div class="widget panel panel-default" ui-sref="app.public.events_show({id: event.id})">
|
||||
<div class="panel-heading picture" style="background-image: url({{event.event_image}});" >
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!event.event_image" class="img-responsive">
|
||||
</div>
|
||||
<div class="panel-body" style="heigth:170px;">
|
||||
<div class="row">
|
||||
<div class="col-xs-9">
|
||||
<h1 class="m-b">{{event.title}}</h1>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<span class="v-middle badge text-xs" ng-class="'bg-{{event.categories[0].name | lowercase}}'">{{event.categories[0].name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{event.description | humanize : 500 }}</p>
|
||||
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 row m-b-sm">
|
||||
<i class="fa fa-calendar red col-xs-3 padder-icon"></i>
|
||||
<h6 class="m-n col-xs-9 ">Du {{event.start_date | amDateFormat:'DD/MM'}}<span class="text-sm font-thin"> au </span>{{event.end_date | amDateFormat:'DD/MM'}}</h6>
|
||||
</div>
|
||||
<div class="col-sm-6 row m-b-sm">
|
||||
<i class="fa fa-clock-o red col-xs-3 padder-icon"></i>
|
||||
<h6 class="m-n col-xs-9"><span ng-if="event.all_day == 'true'">Toute la journée</span><span ng-if="event.all_day == 'false'">De {{event.start_date | date:'HH:mm'}}<span class="text-sm font-thin"> à </span>{{event.end_date | date:'HH:mm'}}</span></h6>
|
||||
</div>
|
||||
<div class="col-sm-12 row m-b">
|
||||
<i class="fa fa-bookmark red col-xs-1 padder-icon"></i>
|
||||
<h6 class="m-n col-xs-10">
|
||||
<span ng-if="!event.nb_total_places">Entrée Libre</span>
|
||||
<span ng-if="event.nb_total_places && event.amount == 0">Entrée Gratuite</span><span ng-if="event.amount > 0">{{event.amount}} € Plein tarif</span><span ng-if="event.reduced_amount > 0"><br/>{{event.reduced_amount}} € Tarif réduit</span>
|
||||
<div ng-if="event.nb_free_places == 0"><span class="badge font-sbold bg-red">Événement complet.</span></div>
|
||||
</h6>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="text-center clearfix ">
|
||||
<div class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm m-b-sm upper text-sm width-70" ui-sref="app.public.events_show({id: event.id})" ><span>Consulter</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
81
app/assets/templates/machines/_form.html.erb
Normal file
@ -0,0 +1,81 @@
|
||||
<form role="form" name="machineForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true" unsaved-warning-form>
|
||||
|
||||
<input name="_method" type="hidden" ng-value="method">
|
||||
|
||||
<section class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
|
||||
<alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</alert>
|
||||
|
||||
<div class="form-group m-b-lg" ng-class="{'has-error': machineForm['machine[name]'].$dirty && machineForm['machine[name]'].$invalid}">
|
||||
<label for="name" class="col-sm-2 control-label">Nom *</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="machine.name" type="text" name="machine[name]" class="form-control" id="machine_name" placeholder="Nom :" required>
|
||||
<span class="help-block" ng-show="machineForm['machine[name]'].$dirty && machineForm['machine[name]'].$error.required">Le Nom est obligatoire.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-b-lg">
|
||||
<label for="machine_image" class="col-sm-2 control-label">Visuel *</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="fileinput" data-provides="fileinput" ng-class="fileinputClass(machine.machine_image)">
|
||||
<div class="fileinput-new thumbnail" style="width: 334px; height: 250px;">
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!machine.machine_image">
|
||||
</div>
|
||||
<div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 334px;">
|
||||
<img ng-src="{{ machine.machine_image }}" alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="btn btn-default btn-file"><span class="fileinput-new">Ajouter un visuel <i class="fa fa-upload fa-fw"></i></span><span class="fileinput-exists">Modifier</span>
|
||||
<input type="file" name="machine[machine_image_attributes][attachment]" ng-model="machine.machine_image" required bs-jasny-fileinput></span>
|
||||
<a class="btn btn-danger fileinput-exists" data-dismiss="fileinput">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group m-b-xl" ng-class="{'has-error': machineForm['machine[description]'].$dirty && machineForm['machine[description]'].$invalid}">
|
||||
<label for="description" class="col-sm-2 control-label">Description *</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea ng-model="machine.description" class="form-control" rows="12" id="machine_description" placeholder="" name="machine[description]" required></textarea>
|
||||
<span class="help-block" ng-show="machineForm['machine[description]'].$dirty && machineForm['machine[description]'].$error.required">La Description est obligatoire.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-b-xl" ng-class="{'has-error': machineForm['machine[spec]'].$dirty && machineForm['machine[spec]'].$invalid}">
|
||||
<label for="spec" class="col-sm-2 control-label">Caractéristiques techniques *</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea ng-model="machine.spec" class="form-control" rows="12" id="machine_spec" placeholder="" name="machine[spec]" required></textarea>
|
||||
<span class="help-block" ng-show="machineForm['machine[spec]'].$dirty && machineForm['machine[spec]'].$error.required">Les Caractéristiques techniques sont obligatoires.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group m-b-xl">
|
||||
<label class="col-sm-2 control-label">Pièces jointes</label>
|
||||
<div class="col-sm-10">
|
||||
<div ng-repeat="file in machine.machine_files_attributes" ng-show="!file._destroy">
|
||||
<input type="hidden" ng-model="file.id" name="machine[machine_files_attributes][][id]" ng-value="file.id" />
|
||||
<input type="hidden" ng-model="file._destroy" name="machine[machine_files_attributes][][_destroy]" ng-value="file._destroy"/>
|
||||
|
||||
<div class="fileinput input-group" data-provides="fileinput" ng-class="fileinputClass(file.attachment)">
|
||||
<div class="form-control" data-trigger="fileinput">
|
||||
<i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">{{file.attachment}}</span>
|
||||
</div>
|
||||
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new">Joindre un fichier</span>
|
||||
<span class="fileinput-exists">Modifier</span><input type="file" name="machine[machine_files_attributes][][attachment]"></span>
|
||||
<a class="input-group-addon btn btn-danger fileinput-exists" data-dismiss="fileinput" ng-click="deleteFile(file)"><i class="fa fa-trash-o"></i></a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a class="btn btn-default" ng-click="addFile()" role="button">Ajouter une pièce jointe <i class="fa fa-file-o fa-fw"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- ./panel-body -->
|
||||
|
||||
<div class="panel-footer no-padder">
|
||||
<input type="submit" value="Valider votre machine" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="machineForm.$invalid"/>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
32
app/assets/templates/machines/edit.html.erb
Normal file
@ -0,0 +1,32 @@
|
||||
<div>
|
||||
|
||||
<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 ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ machine.name }}</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
<div class="btn btn-lg btn-block btn-default rounded m-t-xs" ng-click="cancel()">
|
||||
Annuler
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 b-r-lg nopadding">
|
||||
<ng-include src="'<%= asset_path 'machines/_form.html' %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
59
app/assets/templates/machines/index.html.erb
Normal file
@ -0,0 +1,59 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>Les machines du FabLab</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized('admin')">
|
||||
<section class="heading-actions wrapper">
|
||||
<a class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs" ui-sref="app.admin.machines_new" role="button">Ajouter une machine</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="m-lg">
|
||||
|
||||
<div class="row" ng-repeat="machine in (machines.length/3 | array)">
|
||||
|
||||
<div class="col-xs-12 col-sm-6 col-md-4" ng-repeat="machine in machines.slice(3*$index, 3*$index + 3)">
|
||||
|
||||
|
||||
<div class="widget panel panel-default" ng-click="showMachine(machine)">
|
||||
<div class="panel-heading picture" ng-if="!machine.machine_image">
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder class="img-responsive">
|
||||
</div>
|
||||
<div class="panel-heading picture" style="background-image:url({{machine.machine_image}})" ng-if="machine.machine_image">
|
||||
</div>
|
||||
<div class="panel-body" style="heigth:170px;">
|
||||
<h1 class="m-b">{{machine.name}}</h1>
|
||||
<p>{{machine.description | humanize : 140 }}</p>
|
||||
</div>
|
||||
<div class="panel-footer no-padder">
|
||||
|
||||
<div class="text-center clearfix">
|
||||
<div class="col-sm-12 no-padder">
|
||||
<div class="btn btn-default btn-block padder-v no-b red" ng-click="showMachine(machine)">
|
||||
<i class="fa fa-eye"></i> Consulter
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
29
app/assets/templates/machines/new.html.erb
Normal file
@ -0,0 +1,29 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-md-1 hidden-xs">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-8 b-l b-r">
|
||||
<section class="heading-title">
|
||||
<h1>Déclarer une nouvelle machine</h1>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter" >
|
||||
|
||||
<div class="col-md-9 b-r nopadding">
|
||||
<ng-include src="'<%= asset_path 'machines/_form.html' %>'"></ng-include>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<!-- <button class="btn">TEST</button> -->
|
||||
</div>
|
||||
</div>
|
85
app/assets/templates/machines/show.html.erb
Normal file
@ -0,0 +1,85 @@
|
||||
<div>
|
||||
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-7 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ machine.name }}</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
|
||||
<a ui-sref="app.admin.machines_edit({id: machine.id})" ng-if="isAuthorized('admin')" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs"><i class="fa fa-edit"></i> Éditer</a>
|
||||
<a ng-click="delete(machine)" ng-if="isAuthorized('admin')" class="btn btn-lg btn-danger b-2x rounded no-b m-t-xs"><i class="fa fa-trash-o"></i></a>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-12 col-lg-8 b-r-lg">
|
||||
|
||||
<div class="article wrapper-lg" >
|
||||
|
||||
<div class="article-thumbnail" ng-if="machine.machine_image">
|
||||
<img ng-src="{{machine.machine_image}}" alt="{{machine.name}}" class="img-responsive">
|
||||
</div>
|
||||
|
||||
<!-- <h2>{{machine.name}}</h2> -->
|
||||
<p class="intro" ng-bind-html="machine.description | breakFilter"></p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-4">
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Caractéristiques techniques</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<h3></h3>
|
||||
<p><pre>{{machine.spec}}</pre></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="machine.machine_files_attributes">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{machine.machine_files_attributes.length}}</span>
|
||||
<h3>Fichiers à télécharger</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="file in machine.machine_files_attributes" class="list-group-item no-b clearfix">
|
||||
<a target="_blank" ng-href="{{file.attachment_url}}"><i class="fa fa-arrow-circle-o-down"> </i> {{file.attachment | humanize : 25}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="machine.machine_projects">
|
||||
<div class="panel-heading b-b">
|
||||
<h3>Projets utilisant la machine</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="project in machine.machine_projects" class="list-group-item no-b clearfix">
|
||||
<a ui-sref="app.public.projects_show({id:project.slug})"><i class="fa"> </i> {{project.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
54
app/assets/templates/members/index.html.erb
Normal file
@ -0,0 +1,54 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||
<section class="heading-title">
|
||||
<h1>Les membres du Fab Lab</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="m-lg">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<!-- <button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.members_new">Ajouter un nouveau membre</button> -->
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:15%">Avatar</th>
|
||||
<th style="width:15%">Utilisateur</th>
|
||||
<th style="width:15%">Pseudo</th>
|
||||
<th style="width:15%">Email</th>
|
||||
<th style="width:10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="member in members">
|
||||
<td>
|
||||
<span class="pull-left thumb-sm avatar m-r">
|
||||
<fab-user-avatar ng-model="member.profile.user_avatar" avatar-class="thumb-38"></fab-user-avatar>
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-c">{{ member.name }}</td>
|
||||
<td class="text-u-c text-sm">{{ member.username }}</td>
|
||||
<td>{{ member.email }}</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<button class="btn btn-default" ui-sref="app.logged.members_show({id: member.slug})">
|
||||
<i class="fa fa-eye"></i> Consulter
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
93
app/assets/templates/members/show.html.erb
Normal file
@ -0,0 +1,93 @@
|
||||
<div>
|
||||
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{user.name}}</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
<a ui-sref="app.logged.members" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs">Liste des membres</a>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter wrapper">
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
|
||||
|
||||
|
||||
<div class="wrapper">
|
||||
<section class="widget panel no-border bg-black-light text-white lt">
|
||||
<div class="panel-body">
|
||||
<div class="row m-t-xl">
|
||||
<div class="col-xs-12 text-center">
|
||||
<div class="inline">
|
||||
<div class="easypiechart easyPieChart" data-percent="75" data-line-width="6" data-bar-color="#fff" data-track-color="#2796de" data-scale-color="false" data-size="140" data-line-cap="butt" data-animate="1000" style="width: 140px; height: 140px; line-height: 140px;">
|
||||
<div class="thumb-lg avatar thumb-128-wrapper img">
|
||||
<fab-user-avatar ng-model="user.profile.user_avatar" avatar-class="thumb-140"></fab-user-avatar>
|
||||
</div>
|
||||
<canvas width="140" height="140"></canvas></div>
|
||||
<div class="h4 m-t m-b-xs font-bold text-lt text-white">{{user.name}}</div>
|
||||
<small class="text-muted m-b">{{user.username}}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper m-t-xl m-b">
|
||||
<div class="row m-b">
|
||||
<div class="col-xs-6 text-right">
|
||||
<small>Dernière activité</small>
|
||||
<div class="text-lt font-bold" ng-if="user.last_sign_in_at">le {{user.last_sign_in_at | amDateFormat: 'Do MMMM '}}</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<small>Email</small>
|
||||
<div class="text-lt font-bold break-word">{{user.email}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 text-right">
|
||||
<small>Logiciels de conception maîtrisés</small>
|
||||
<div class="text-lt font-bold">{{user.profile.software_mastered}}</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<small>Centres d'intérêts</small>
|
||||
<div class="text-lt font-bold">{{user.profile.interest}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
|
||||
<div class="widget panel b-a m ">
|
||||
<div class="panel-heading b-b">
|
||||
<h1 class="red text-u-c">Projets</h1>
|
||||
<ul class="list-unstyled" ng-if="user.all_projects.length > 0">
|
||||
<li ng-repeat="p in user.all_projects" class="m-t-sm">
|
||||
<a class="text-u-c" ui-sref="app.public.projects_show({id:p.slug})" role="button">{{p.name}} <span class="m-l-sm label label-success text-white">{{p.author_id == currentUser.id ? 'Auteur' : 'Collaborateur'}}</span></a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div ng-if="user.all_projects.length == 0">Aucun projet</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
91
app/assets/templates/notifications/index.html.erb
Normal file
@ -0,0 +1,91 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||
<section class="heading-title">
|
||||
<h1>Centre de notifications</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="m-lg">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-warning m-t-sm m-b" ng-click="markAllAsRead()" ng-disabled="notifications.length == 0">Tout marquer comme lu ({{notifications.length}})</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:10%"></th>
|
||||
<th style="width:20%">Date</th>
|
||||
<th style="width:70%">Intitulée</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="notification in notifications" ng-if="notifications.length > 0">
|
||||
<td>
|
||||
<button class="btn btn-sm btn-warning" ng-click="markAsRead(notification, $event)">
|
||||
<i class="fa fa-check"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td>{{ notification.created_at | amDateFormat:'D MMMM YYYY H:mm' }}</td>
|
||||
<td ng-bind-html="notification.message.description"></td>
|
||||
|
||||
</tr>
|
||||
<tr ng-if="notifications.length == 0">
|
||||
<td colspan="3">Aucune nouvelle notification.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>Archives</h5>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:10%"></th>
|
||||
<th style="width:20%"></th>
|
||||
<th style="width:70%"></th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
|
||||
<tr class="read" ng-repeat="n in notificationsRead | orderBy:'created_at':true" ng-if="notificationsRead.length > 0">
|
||||
<td>
|
||||
</td>
|
||||
<td>{{ n.created_at | amDateFormat:'D MMMM YYYY H:mm' }}</td>
|
||||
<td ng-bind-html="n.message.description"></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
<tr ng-if="notificationsRead.length == 0">
|
||||
<td colspan="3">Aucune notification archivée.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<a class="btn btn-default" ng-click="addMoreNotificationsReaded()" ng-if="paginateActive">Charger les notifications suivantes...</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
188
app/assets/templates/projects/_form.html.erb
Normal file
@ -0,0 +1,188 @@
|
||||
<div class="row no-gutter">
|
||||
|
||||
<div class=" col-sm-12 col-md-12 col-lg-9 nopadding">
|
||||
|
||||
<section class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
|
||||
<alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</alert>
|
||||
|
||||
<input name="_method" type="hidden" ng-value="method">
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': projectForm['project[name]'].$dirty && projectForm['project[name]'].$invalid}">
|
||||
<label for="name" class="col-sm-2 control-label">Nom *</label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-model="project.name" type="text" name="project[name]" class="form-control" id="project_name" placeholder="" required>
|
||||
<span class="help-block" ng-show="projectForm['project[name]'].$dirty && projectForm['project[name]'].$error.required">Nom est obligatoire</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="project_image" class="col-sm-2 control-label">Illustration</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="fileinput" data-provides="fileinput" ng-class="fileinputClass(project.project_image)">
|
||||
<div class="fileinput-new thumbnail" style="width: 334px; height: 250px;">
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!project.project_image">
|
||||
</div>
|
||||
<div class="fileinput-preview fileinput-exists thumbnail" data-trigger="fileinput" style="max-width: 334px;">
|
||||
<img ng-src="{{ project.project_image }}" alt="" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="btn btn-default btn-file"><span class="fileinput-new">Ajouter un visuel <i class="fa fa-upload fa-fw"></i></span><span class="fileinput-exists">Modifier</span>
|
||||
<input type="file" name="project[project_image_attributes][attachment]"></span>
|
||||
<a class="btn btn-danger fileinput-exists" data-dismiss="fileinput">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Fichier CAO</label>
|
||||
<div class="col-sm-10">
|
||||
<div ng-repeat="file in project.project_caos_attributes" ng-show="!file._destroy">
|
||||
<input type="hidden" name="project[project_caos_attributes][][id]" ng-value="file.id" />
|
||||
<input type="hidden" name="project[project_caos_attributes][][_destroy]" ng-value="file._destroy" />
|
||||
|
||||
<div class="fileinput input-group" data-provides="fileinput" ng-class="fileinputClass(file.attachment)">
|
||||
<div class="form-control" data-trigger="fileinput">
|
||||
<i class="glyphicon glyphicon-file fileinput-exists"></i> <span class="fileinput-filename">{{file.attachment}}</span>
|
||||
</div>
|
||||
<span class="input-group-addon btn btn-default btn-file"><span class="fileinput-new">Parcourir</span>
|
||||
<span class="fileinput-exists">Modifier</span><input type="file" name="project[project_caos_attributes][][attachment]"></span>
|
||||
<a class="input-group-addon btn btn-danger fileinput-exists" data-dismiss="fileinput" ng-click="deleteFile(file)"><i class="fa fa-trash-o"></i></a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a class="btn btn-default" ng-click="addFile()" role="button">Ajouter un nouveau fichier <i class="fa fa-file-o fa-fw"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': projectForm['project[description]'].$dirty && projectForm['project[description]'].$invalid}">
|
||||
<label for="description" class="col-sm-2 control-label">Description *</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea ng-model="project.description" rows="16" class="" id="project_description" placeholder="" name="project[description]" redactor required></textarea>
|
||||
<span class="help-block" ng-show="projectForm['project[description]'].$dirty && projectForm['project[description]'].$error.required">Description est obligatoire</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Étapes</label>
|
||||
<div class="col-sm-10">
|
||||
<div ng-repeat="step in project.project_steps_attributes" ng-show="!step._destroy">
|
||||
<div class="m-t-xs m-b-lg">
|
||||
<span class="label label-warning m-t m-b">Étape {{ $index+1 }}/{{project.project_steps_attributes.length}}</span>
|
||||
<input type="hidden" name="project[project_steps_attributes][][id]" ng-value="step.id" />
|
||||
<input type="hidden" name="project[project_steps_attributes][][_destroy]" ng-value="step._destroy" />
|
||||
<input ng-model="step.title" type="text" name="project[project_steps_attributes][][title]" class="form-control m-b-sm m-t-xs" placeholder="Titre de l'étape" required>
|
||||
<textarea name="project[project_steps_attributes][][description]" ng-model="step.description" class="m-b-sm form-control" placeholder="" rows="8" redactor></textarea>
|
||||
|
||||
<div class="fileinput" data-provides="fileinput" ng-class="fileinputClass(step.project_step_image)">
|
||||
<span class="btn btn-default btn-file"><span class="fileinput-new">Ajouter une image</span><span class="fileinput-exists">Modifier l'image</span>
|
||||
<input type="file" name="project[project_steps_attributes][][project_step_image_attributes][attachment]"></span>
|
||||
<span class="fileinput-filename">{{step.project_step_image}}</span>
|
||||
<a class="close fileinput-exists" data-dismiss="fileinput" style="float: none"><i class="fa fa-trash-o"></i></a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a class="btn btn-sm btn-danger" ng-click="deleteStep(step)" role="button"><i class="fa fa-trash-o"></i> Supprimer l'étape</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-default m-b" ng-click="addStep()" role="button">Ajouter une nouvelle étape</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div> <!-- ./panel-body -->
|
||||
<div class="panel-footer no-padder">
|
||||
<div ng-show="project.state != 'published'">
|
||||
<div class="btn btn-lg btn-block btn-valid btn-success text-u-c r-n" publish-project ng-disabled="projectForm.$invalid">
|
||||
Publier votre projet
|
||||
</div>
|
||||
<div class="text-center font-bold text-u-c">ou</div>
|
||||
</div>
|
||||
<input type="submit" ng-value="submitName" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="projectForm.$invalid"/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Matériaux utilisés</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<input type="hidden" name="project[component_ids][]" value="" />
|
||||
<select ng-model="project.component_ids" class="form-control form-control-ui-select" name="project[component_ids][]" ui-select2 multiple>
|
||||
<option value="{{c.id}}" ng-repeat="c in components">{{c.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Machines utilisées</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<input type="hidden" name="project[machine_ids][]" value="" />
|
||||
<select ng-model="project.machine_ids" class="form-control form-control-ui-select" name="project[machine_ids][]" ui-select2 multiple>
|
||||
<option value="{{m.id}}" ng-repeat="m in machines">{{m.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Collaborateurs</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<input type="hidden" name="project[user_ids][]" value="" />
|
||||
<select ng-model="project.user_ids" class="form-control form-control-ui-select" name="project[user_ids][]" ui-select2 multiple>
|
||||
<option value="{{m.id}}" ng-repeat="m in members">{{m.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Licences Creative Commons</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<%# TODO: Bug concerne qu'on ne peut pas déselectionner un option %>
|
||||
<select ng-model="project.licence_id" class="form-control" name="project[licence_id]" ui-select2>
|
||||
<option value="{{l.id}}" ng-repeat="l in licences">{{l.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Thématiques</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<input type="hidden" name="project[theme_ids][]" value="" />
|
||||
<select ng-model="project.theme_ids" class="form-control form-control-ui-select" name="project[theme_ids][]" ui-select2 multiple>
|
||||
<option value="{{t.id}}" ng-repeat="t in themes">{{t.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3>Tags</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg wrapper">
|
||||
<textarea ng-model="project.tags" class="form-control" id="project_tags" placeholder="" name="project[tags]"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
40
app/assets/templates/projects/edit.html.erb
Normal file
@ -0,0 +1,40 @@
|
||||
<div>
|
||||
|
||||
|
||||
<form role="form" name="projectForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true" unsaved-warning-form>
|
||||
|
||||
<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 ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-7 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>Editer le projet <span class="badge" ng-if="project.state == 'draft'">Brouillon</span></h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-4 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
<!-- <div class="btn btn-lg btn-block btn-default m-t-xs" ng-click="cancel()" ng-if="project.state == 'published'">
|
||||
Annuler
|
||||
</div> -->
|
||||
<input type="submit" ng-value="submitName" class="btn btn-lg btn-warning m-t-xs text-u-c" ng-disabled="projectForm.$invalid"/>
|
||||
<div class="btn btn-lg btn-valid btn-success m-t-xs text-u-c" publish-project ng-if="project.state == 'draft'" ng-disabled="projectForm.$invalid">
|
||||
Publier
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<ng-include src="'<%= asset_path 'projects/_form.html' %>'"></ng-include>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
98
app/assets/templates/projects/index.html.erb
Normal file
@ -0,0 +1,98 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>Les projets du FabLab</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin','member'])">
|
||||
<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.logged.projects_new" role="button">Ajouter un projet</a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="!isAuthenticated()">
|
||||
<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.logged.projects_new" role="button">Proposer un projet</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="m-lg">
|
||||
|
||||
<div class="row m-b-md">
|
||||
|
||||
<div class="col-md-12"><h3 class="m-t-xs">Filtrer les projets</h3></div>
|
||||
|
||||
<div class="col-md-3 m-b" ng-show="isAuthenticated()">
|
||||
<select ng-model="selectedMember" class="form-control">
|
||||
<option value="">Tous les projets</option>
|
||||
<option value="0">Mes projets</option>
|
||||
<option value="1">Les projets auxquels je collabore</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 m-b">
|
||||
<select ng-model="selectedMachine" class="form-control" ng-options="m.id as m.name for m in machines">
|
||||
<option value="">Toutes les machines</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 m-b">
|
||||
<select ng-model="selectedTheme" class="form-control" ng-options="t.id as t.name for t in themes">
|
||||
<option value="">Toutes les thématiques</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 m-b">
|
||||
<select ng-model="selectedComponent" class="form-control" ng-options="t.id as t.name for t in components">
|
||||
<option value="">Tous les matériaux</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-xs-12 col-sm-6 col-md-4" ng-repeat="project in filtered = (projects | machineFilter:selectedMachine | projectMemberFilter:selectedMember | themeFilter:selectedTheme | componentFilter:selectedComponent)" ng-click="showProject(project)">
|
||||
|
||||
<div class="box-thumb box-thumb-project" style="background-image: url({{project.project_image}});">
|
||||
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!project.project_image">
|
||||
|
||||
<div class="box-content project-caption">
|
||||
<h1>{{project.name}}</h1>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<div class="btn-group">
|
||||
<div class="btn btn-default" ui-sref="app.logged.projects_edit({id:project.id})" ng-if="projectEditableBy(currentUser) || isAuthorized('admin')">
|
||||
<i class="fa fa-edit"></i> Éditer
|
||||
</div>
|
||||
<div class="btn btn-default" ng-click="showProject(project)">
|
||||
<i class="fa fa-eye"></i> Consulter
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12 text-center">
|
||||
<a class="btn btn-warning" ng-click="loadMoreProjects()" ng-if="paginateActive">Charger les projets suivants ...</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
33
app/assets/templates/projects/new.html.erb
Normal file
@ -0,0 +1,33 @@
|
||||
<div>
|
||||
|
||||
<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 ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l ">
|
||||
<section class="heading-title">
|
||||
<h1>Ajouter un nouveau projet</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- <div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
<div class="btn btn-lg btn-block btn-valid btn-info m-t-xs text-u-c" publish-project ng-disabled="projectForm.$invalid">
|
||||
Publier
|
||||
</div>
|
||||
</section>
|
||||
</div> -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<form role="form" name="projectForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
|
||||
|
||||
<ng-include src="'<%= asset_path 'projects/_form.html' %>'"></ng-include>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
141
app/assets/templates/projects/show.html.erb
Normal file
@ -0,0 +1,141 @@
|
||||
<div>
|
||||
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l b-r-md">
|
||||
<section class="heading-title">
|
||||
<h1>{{ project.name }} <span class="badge" ng-if="project.state == 'draft'">Brouillon</span></h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
|
||||
<a ui-sref="app.logged.projects_edit({id: project.id})" ng-if="projectEditableBy(currentUser) || isAuthorized('admin')" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs text-u-c text-sm"><i class="fa fa-edit"></i> éditer</a>
|
||||
<a ng-if="!isAuthenticated()" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm upper text-sm" ui-sref="app.logged.projects_new" role="button">Proposer un projet</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 b-r-lg">
|
||||
|
||||
<div class="article wrapper-lg">
|
||||
|
||||
<div class="article-thumbnail" ng-if="project.project_image">
|
||||
<a href="{{project.project_image}}" target="_blank"><img ng-src="{{project.project_image}}" alt="{{project.name}}" class="img-responsive"></a>
|
||||
</div>
|
||||
|
||||
<h3>Description du projet</h3>
|
||||
<p ng-bind-html="project.description | toTrusted"></p>
|
||||
|
||||
<div class="article-steps">
|
||||
<div class="row article-step m-b-lg" ng-repeat="step in project.project_steps_attributes">
|
||||
<div class="col-md-12 m-b-xs">
|
||||
<h3 class="well well-simple step-title">Étape {{$index+1}} : {{step.title}}</h3>
|
||||
</div>
|
||||
<div class="col-md-4" ng-if="step.project_step_image">
|
||||
<a href="{{step.project_step_image_url}}" target="_blank"><img class="img-responsive m-b" ng-src="{{step.project_step_image_url}}" alt="{{step.title}}" ></a>
|
||||
</div>
|
||||
<div class="col-md-8" ng-class="{'col-md-12' : step.project_step_image == undefined}">
|
||||
|
||||
<p ng-bind-html="step.description | toTrusted"></p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="wrapper-lg">
|
||||
<dir-disqus disqus-shortname="<%= Rails.application.secrets.disqus_shortname %>"
|
||||
disqus-identifier="project_{{ project.id }}"
|
||||
disqus-url="{{ project_url }}"
|
||||
ready-to-bind="{{ contentLoaded }}">
|
||||
</dir-disqus>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
|
||||
|
||||
<div class="text-center m-t-lg m-v">
|
||||
<div class="thumb-lg m-b-xs">
|
||||
<fab-user-avatar ng-model="project.author.user_avatar" avatar-class="thumb-50"></fab-user-avatar>
|
||||
</div>
|
||||
<div><a class="text-sm font-sbold" ui-sref="app.logged.members_show({id: project.author.slug})"><i>Par {{project.author.first_name}}</i></a></div>
|
||||
<small class="text-xs m-b"><i>posté le {{project.created_at | amDateFormat: 'Do MMMM YYYY'}}</i></small>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_caos_attributes">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.project_caos_attributes.length}}</span>
|
||||
<h3>Fichier CAO à télécharger</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="file in project.project_caos_attributes" class="list-group-item no-b clearfix">
|
||||
<a target="_blank" ng-href="{{file.attachment_url}}" download="{{file.attachment_url}}"><i class="fa fa-arrow-circle-o-down"> </i> {{file.attachment | humanize : 25}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.machines">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.machines.length}}</span>
|
||||
<h3>Machines et matériaux</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="machine in project.machines" class="list-group-item no-b clearfix">
|
||||
<a ui-sref="app.public.machines_show({id: machine.id})">{{machine.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li ng-repeat="component in project.components" class="list-group-item no-b clearfix">
|
||||
{{component.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="widget panel b-a m" ng-if="project.project_users.length > 0">
|
||||
<div class="panel-heading b-b">
|
||||
<span class="badge bg-warning pull-right">{{project.project_users.length}}</span>
|
||||
<h3>Les collaborateurs</h3>
|
||||
</div>
|
||||
|
||||
<ul class="widget-content list-group list-group-lg no-bg auto">
|
||||
<li class="list-group-item no-b clearfix block-link" ng-repeat="collaborator in project.project_users" ui-sref="app.logged.members_show({id: collaborator.slug})">
|
||||
<span class="pull-left thumb-sm avatar m-r">
|
||||
<fab-user-avatar ng-model="collaborator.user_avatar" avatar-class="thumb-38"></fab-user-avatar>
|
||||
|
||||
<i class="on b-white bottom" ng-if="collaborator.is_valid"></i>
|
||||
<i class="off b-white bottom" ng-if="!collaborator.is_valid"></i>
|
||||
</span>
|
||||
<span class="clear"><span>{{collaborator.full_name}}</span>
|
||||
<small class="text-muted clear text-ellipsis text-c">{{collaborator.username}}</small>
|
||||
</span>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
145
app/assets/templates/shared/_member_form.html.erb
Normal file
@ -0,0 +1,145 @@
|
||||
<alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</alert>
|
||||
|
||||
<input name="_method" type="hidden" ng-value="method">
|
||||
<input name="user[profile_attributes][id]" type="hidden" ng-value="user.profile.id">
|
||||
|
||||
<div class="row m-t">
|
||||
<div class="col-sm-3 col-sm-offset-1">
|
||||
<div class="form-group m-t-lg">
|
||||
<div class="fileinput text-center" data-provides="fileinput" ng-class="fileinputClass(user.profile.user_avatar.attachment_url)">
|
||||
<div class="fileinput-new thumbnail rounded thumb-128-wrapper" style="width: 140px; height: 140px;">
|
||||
<img src="<%= image_path("no_avatar.png") %>" class="img-circle">
|
||||
</div>
|
||||
<div class="fileinput-preview fileinput-exists thumbnail rounded thumb-128-wrapper" data-trigger="fileinput" style="width: 140px; height: 140px; line-height: 140px;">
|
||||
<img ng-src="{{ user.profile.user_avatar.attachment_url }}" />
|
||||
</div>
|
||||
<div class="m-t-sm">
|
||||
<input type="hidden" name="user[profile_attributes][user_avatar_attributes][id]" ng-value="user.profile.user_avatar.id">
|
||||
<input type="hidden" name="user[profile_attributes][user_avatar_attributes][_destroy]" ng-value="true" ng-if="user.profile.user_avatar._destory">
|
||||
<span class="btn btn-default btn-file" ng-click="user.profile.user_avatar._destory = false"><span class="fileinput-new">Ajouter un avatar</span><span class="fileinput-exists">Modifier</span>
|
||||
<input type="file" name="user[profile_attributes][user_avatar_attributes][attachment]"></span>
|
||||
<button class="btn btn-danger fileinput-exists" data-dismiss="fileinput" ng-click="user.profile.user_avatar._destory = true"><i class="fa fa-trash-o"></i> </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-sm-offset-1 col-sm-6">
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[profile_attributes][gender]'].$dirty && userForm['user[profile_attributes][gender]'].$invalid}">
|
||||
<label class="checkbox-inline btn btn-default">
|
||||
<input type="radio" name="user[profile_attributes][gender]" ng-model="user.profile.gender" value="true" required/><i class="fa fa-male m-l-sm"></i> Homme
|
||||
</label>
|
||||
<label class="checkbox-inline btn btn-default">
|
||||
<input type="radio" name="user[profile_attributes][gender]" ng-model="user.profile.gender" value="false"/> <i class="fa fa-female m-l-sm"></i> Femme
|
||||
</label>
|
||||
<span class="help-block" ng-show="userForm['user[profile_attributes][gender]'].$dirty && userForm['user[profile_attributes][gender]'].$error.required">Le genre est obligatoire</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[username]'].$dirty && userForm['user[username]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i></span>
|
||||
<input ng-model="user.username" type="text" name="user[username]" class="form-control" id="user_username" placeholder="Pseudo" required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[username]'].$dirty && userForm['user[username]'].$error.required">Le pseudo est obligatoire</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[profile_attributes][last_name]'].$dirty && userForm['user[profile_attributes][last_name]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i></span>
|
||||
<input ng-model="user.profile.last_name" type="text" name="user[profile_attributes][last_name]" class="form-control" id="user_last_name" placeholder="Nom" required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[profile_attributes][last_name]'].$dirty && userForm['user[profile_attributes][last_name]'].$error.required">Le nom est obligatoire</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[profile_attributes][first_name]'].$dirty && userForm['user[profile_attributes][first_name]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i></span>
|
||||
<input ng-model="user.profile.first_name" type="text" name="user[profile_attributes][first_name]" class="form-control" id="user_first_name" placeholder="Prénom" required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[profile_attributes][first_name]'].$dirty && userForm['user[profile_attributes][first_name]'].$error.required">Le prénom est obligatoire</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[email]'].$dirty && userForm['user[email]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-envelope"></i> </span>
|
||||
<input ng-model="user.email" type="email" name="user[email]" class="form-control" id="user_email" placeholder="Adresse email" required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[email]'].$dirty && userForm['user[email]'].$error.required">L'email est obligatoire</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="btn btn-warning btn-block" ng-click="change_password = !change_password; $event.stopPropagation(); $event.preventDefault()">Changer de mot de passe</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[password]'].$dirty && userForm['user[password]'].$invalid}" ng-if="change_password">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-key"></i> </span>
|
||||
<input ng-model="user.password" type="password" name="user[password]" class="form-control" id="user_password" placeholder="Nouveau mot de passe" required ng-minlength="8">
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[password]'].$dirty && userForm['user[password]'].$error.required">Le mot de passe est obligatoire</span>
|
||||
<span class="help-block" ng-show="userForm['user[password]'].$dirty && userForm['user[password]'].$error.minlength">Le mot de passe est trop court (au moins 8 caractères)</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[password_confirmation]'].$dirty && userForm['user[password_confirmation]'].$invalid}" ng-if="change_password">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-key"></i> </span>
|
||||
<input ng-model="user.password_confirmation" type="password" name="user[password_confirmation]" class="form-control" id="user_password_confirmation" placeholder="Confirmation du nouveau mot de passe" required ng-minlength="8">
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[password_confirmation]'].$dirty && userForm['user[password_confirmation]'].$error.required">Le mot de passe de confirmation est obligatoire</span>
|
||||
<span class="help-block" ng-show="userForm['user[password_confirmation]'].$dirty && userForm['user[password_confirmation]'].$error.minlength">Le mot de passe de confirmation est trop court (au moins 8 caractères)</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[profile_attributes][birthday]'].$dirty && userForm['user[profile_attributes][birthday]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-calendar-o"></i> </span>
|
||||
<input type="text"
|
||||
id="user_birthday"
|
||||
class="form-control"
|
||||
name="user[profile_attributes][birthday]"
|
||||
ng-model="user.profile.birthday"
|
||||
datepicker-popup="dd/MM/yyyy"
|
||||
datepicker-options="datePicker.options"
|
||||
is-open="datePicker.opened"
|
||||
placeholder="Date de naissance"
|
||||
ng-click="openDatePicker($event)"
|
||||
required/>
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[profile_attributes][birthday]'].$dirty && userForm['user[profile_attributes][birthday]'].$error.required">La date de naissance est obligatoire</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-map-marker"></i> </span>
|
||||
<input type="hidden" name="user[profile_attributes][address_attributes][id]" ng-value="user.profile.address.id" />
|
||||
<input ng-model="user.profile.address.address" type="text" name="user[profile_attributes][address_attributes][address]" class="form-control" id="user_address" placeholder="Adresse">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': userForm['user[profile_attributes][phone]'].$dirty && userForm['user[profile_attributes][phone]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-phone"></i> </span>
|
||||
<input ng-model="user.profile.phone" type="text" name="user[profile_attributes][phone]" class="form-control" id="user_phone" placeholder="Numéro de téléphone" required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="userForm['user[profile_attributes][phone]'].$dirty && userForm['user[profile_attributes][phone]'].$error.required">Le numéro de téléphone est obligatoire.</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="user_interest">Centres d'intérêts</label>
|
||||
<textarea ng-model="user.profile.interest" rows="5" name="user[profile_attributes][interest]" class="form-control" id="user_interest" placeholder=""></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="user_software_mastered">Logiciels de conception maîtrisés</label>
|
||||
<textarea ng-model="user.profile.software_mastered" rows="5" name="user[profile_attributes][software_mastered]" class="form-control" id="user_software_mastered" placeholder=""></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
11
app/assets/templates/shared/_member_select.html.erb
Normal file
@ -0,0 +1,11 @@
|
||||
<div class="widget panel b-a m">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3 class="panel-title">Sélectionnez un membre</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg auto wrapper">
|
||||
<select ng-model="ctrl.member_id" class="form-control form-control-ui-select" ui-select2 ng-change="updateMember()">
|
||||
<option value="{{$index}}" ng-repeat="m in members">{{m.name}}</option>
|
||||
</select>
|
||||
{{member}}
|
||||
</div>
|
||||
</div>
|
4
app/assets/templates/shared/_user_avatar.html.erb
Normal file
@ -0,0 +1,4 @@
|
||||
<img ng-src="<%= image_path("no_avatar.png") %>" class="img-circle" ng-class="avatarClass"
|
||||
ng-if="!userAvatar || !userAvatar.attachment_url">
|
||||
<img ng-src="{{userAvatar.attachment_url}}" class="img-circle" ng-class="avatarClass"
|
||||
ng-if="userAvatar.attachment_url">
|