Compare commits
116 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
193ad1bd44 | ||
|
fec6c7d204 | ||
|
1c5c5b1cc7 | ||
|
65f103b22c | ||
|
51fbef582f | ||
|
a769142d2f | ||
|
6dd89038d3 | ||
|
d04a47541f | ||
|
3f008c6c14 | ||
|
464f2e5d5d | ||
|
12c6dbd0a3 | ||
|
a4697dc143 | ||
|
78942c3589 | ||
|
9f4abcb06d | ||
|
0af4b6fb46 | ||
|
1fc5e9afcd | ||
|
a23b58df49 | ||
|
3fa27602a9 | ||
|
2e84cc1c28 | ||
|
a030fc2854 | ||
|
b14a5e1b18 | ||
|
c874a2ea35 | ||
|
50278c5cb6 | ||
|
ebb7711ee6 | ||
|
e09e721bee | ||
|
3821b72b16 | ||
|
6f60f63252 | ||
|
3d26f604d7 | ||
|
ff5d2a3208 | ||
|
093e4793d7 | ||
|
f3ae39f3f3 | ||
|
4cf206d227 | ||
|
774c5e9884 | ||
|
d57ba8ad44 | ||
|
f7e44aacd7 | ||
|
a68a73f641 | ||
|
7646e982d3 | ||
|
2a7f23d2c5 | ||
|
afd21fdbb7 | ||
|
4996a56dd6 | ||
|
0ea64494d3 | ||
|
81069a8595 | ||
|
fbf54dd98b | ||
|
63dcc3d08d | ||
|
bee97dbfd7 | ||
|
66c5022e41 | ||
|
a63e608a1e | ||
|
169d780bad | ||
|
909cd70f08 | ||
|
45c8d8cf71 | ||
|
b33dd3f3cc | ||
|
b9533a8fc1 | ||
|
7b380adc24 | ||
|
3ec0c0537a | ||
|
1a88a3165d | ||
|
88b4c48d5a | ||
|
a191f51e94 | ||
|
7838150f59 | ||
|
f84c3bc0da | ||
|
4121db9310 | ||
|
a1b57336c8 | ||
|
b6a83ece53 | ||
|
af9c5ee603 | ||
|
7d5986d2e3 | ||
|
bb2b1499f0 | ||
|
91042ba224 | ||
|
3a061b95af | ||
|
e060479af6 | ||
|
1db9817e82 | ||
|
04306131ba | ||
|
abebdca3fe | ||
|
41fb5e5f05 | ||
|
3809f488f7 | ||
|
c4ea0a5743 | ||
|
4e4114a613 | ||
|
7e7b60556e | ||
|
ed23d39eaa | ||
|
f3e630e429 | ||
|
ab14f40008 | ||
|
7de0bcc906 | ||
|
5664e2b895 | ||
|
0f13cbf00d | ||
|
d4bab51106 | ||
|
6da3abcff7 | ||
|
3ec60f271d | ||
|
cd30ddec84 | ||
|
ea90eebc0b | ||
|
666f897a31 | ||
|
ca623c0578 | ||
|
b59006bd65 | ||
|
e7b7fa4d1a | ||
|
9d7c5775e1 | ||
|
81f02e27fe | ||
|
eb940544f4 | ||
|
bb09ef21d8 | ||
|
f6246ef9d1 | ||
|
1c382ef814 | ||
|
61f8deeb22 | ||
|
7566b6e332 | ||
|
6889f34d87 | ||
|
a368ffbfc5 | ||
|
84ae6df2b3 | ||
|
854d9005ab | ||
|
cf0a78b042 | ||
|
05c194854c | ||
|
36f357b56e | ||
|
111ea11874 | ||
|
48c2ae31df | ||
|
6b482929ee | ||
|
815e238ee0 | ||
|
f9bf9b6056 | ||
|
dfa26f8aa8 | ||
|
ab53be3d49 | ||
|
93f58bffe6 | ||
|
63d74310f6 | ||
|
703f291e82 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -23,3 +23,6 @@ erd.pdf
|
||||||
config/database.yml
|
config/database.yml
|
||||||
.env
|
.env
|
||||||
.env*
|
.env*
|
||||||
|
|
||||||
|
lolcommit.yml
|
||||||
|
*.swp
|
||||||
|
|
30
Gemfile
30
Gemfile
|
@ -1,6 +1,5 @@
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
|
||||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||||
gem 'rails', '4.2.1'
|
gem 'rails', '4.2.1'
|
||||||
# Use sqlite3 as the database for Active Record
|
# Use sqlite3 as the database for Active Record
|
||||||
|
@ -16,6 +15,7 @@ gem 'coffee-rails', '~> 4.1.0'
|
||||||
|
|
||||||
# Use jquery as the JavaScript library
|
# Use jquery as the JavaScript library
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails'
|
||||||
|
gem 'jquery-ui-rails'
|
||||||
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
||||||
gem 'turbolinks'
|
gem 'turbolinks'
|
||||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||||
|
@ -24,7 +24,7 @@ gem 'jbuilder', '~> 2.0'
|
||||||
# bundle exec rake doc:rails generates the API under doc/api.
|
# bundle exec rake doc:rails generates the API under doc/api.
|
||||||
group :doc do
|
group :doc do
|
||||||
gem 'sdoc', '~> 0.4.0'
|
gem 'sdoc', '~> 0.4.0'
|
||||||
gem 'erd'
|
gem 'rails-erd'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use ActiveModel has_secure_password
|
# Use ActiveModel has_secure_password
|
||||||
|
@ -50,19 +50,25 @@ end
|
||||||
|
|
||||||
# Web Server
|
# Web Server
|
||||||
gem 'puma'
|
gem 'puma'
|
||||||
# Database
|
|
||||||
gem 'pg'
|
|
||||||
# Upload
|
|
||||||
gem 'carrierwave'
|
|
||||||
# Environnement .env
|
# Environnement .env
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
|
# Database
|
||||||
|
gem 'pg'
|
||||||
|
|
||||||
# Users
|
# Users
|
||||||
gem 'devise'
|
gem 'devise'
|
||||||
# Pagination
|
# Upload
|
||||||
gem 'will_paginate'
|
gem 'carrierwave'
|
||||||
# Bootstrap
|
# Commentable items
|
||||||
gem 'bootstrap-sass'
|
gem 'acts_as_commentable'
|
||||||
# Bootstrap for pagination
|
|
||||||
gem 'bootstrap-will_paginate'
|
|
||||||
# HTML Templating
|
# HTML Templating
|
||||||
gem 'slim-rails'
|
gem 'slim-rails'
|
||||||
|
# Bootstrap
|
||||||
|
gem "twitter-bootstrap-rails"
|
||||||
|
# Pagination
|
||||||
|
gem 'will_paginate'
|
||||||
|
# Bootstrap for pagination
|
||||||
|
gem 'bootstrap-will_paginate'
|
||||||
|
# Autocomplete for ids
|
||||||
|
gem 'rails-jquery-autocomplete'
|
||||||
|
|
58
Gemfile.lock
58
Gemfile.lock
|
@ -36,16 +36,11 @@ GEM
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
thread_safe (~> 0.3, >= 0.3.4)
|
thread_safe (~> 0.3, >= 0.3.4)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
arel (6.0.0)
|
acts_as_commentable (4.0.2)
|
||||||
autoprefixer-rails (5.2.0.1)
|
arel (6.0.3)
|
||||||
execjs
|
|
||||||
json
|
|
||||||
bcrypt (3.1.10)
|
bcrypt (3.1.10)
|
||||||
binding_of_caller (0.7.2)
|
binding_of_caller (0.7.2)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
bootstrap-sass (3.3.5)
|
|
||||||
autoprefixer-rails (>= 5.0.0.1)
|
|
||||||
sass (>= 3.2.19)
|
|
||||||
bootstrap-will_paginate (0.0.10)
|
bootstrap-will_paginate (0.0.10)
|
||||||
will_paginate
|
will_paginate
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
|
@ -74,24 +69,24 @@ GEM
|
||||||
responders
|
responders
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
dotenv (2.0.1)
|
dotenv (2.0.2)
|
||||||
dotenv-rails (2.0.1)
|
dotenv-rails (2.0.2)
|
||||||
dotenv (= 2.0.1)
|
dotenv (= 2.0.2)
|
||||||
erd (0.3.2)
|
railties (~> 4.0)
|
||||||
nokogiri
|
|
||||||
rails-erd (>= 0.4.5)
|
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
execjs (2.5.2)
|
execjs (2.5.2)
|
||||||
globalid (0.3.5)
|
globalid (0.3.6)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
i18n (0.7.0)
|
i18n (0.7.0)
|
||||||
jbuilder (2.3.0)
|
jbuilder (2.3.1)
|
||||||
activesupport (>= 3.0.0, < 5)
|
activesupport (>= 3.0.0, < 5)
|
||||||
multi_json (~> 1.2)
|
multi_json (~> 1.2)
|
||||||
jquery-rails (4.0.4)
|
jquery-rails (4.0.4)
|
||||||
rails-dom-testing (~> 1.0)
|
rails-dom-testing (~> 1.0)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
|
jquery-ui-rails (5.0.5)
|
||||||
|
railties (>= 3.2.16)
|
||||||
json (1.8.3)
|
json (1.8.3)
|
||||||
loofah (2.0.2)
|
loofah (2.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
|
@ -100,8 +95,8 @@ GEM
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
mime-types (2.6.1)
|
mime-types (2.6.1)
|
||||||
mini_portile (0.6.2)
|
mini_portile (0.6.2)
|
||||||
minitest (5.7.0)
|
minitest (5.8.0)
|
||||||
multi_json (1.11.1)
|
multi_json (1.11.2)
|
||||||
nokogiri (1.6.6.2)
|
nokogiri (1.6.6.2)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
|
@ -110,9 +105,8 @@ GEM
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.8.1)
|
method_source (~> 0.8.1)
|
||||||
slop (~> 3.4)
|
slop (~> 3.4)
|
||||||
puma (2.11.3)
|
puma (2.12.3)
|
||||||
rack (>= 1.1, < 2.0)
|
rack (1.6.4)
|
||||||
rack (1.6.2)
|
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (4.2.1)
|
rails (4.2.1)
|
||||||
|
@ -132,13 +126,15 @@ GEM
|
||||||
activesupport (>= 4.2.0.beta, < 5.0)
|
activesupport (>= 4.2.0.beta, < 5.0)
|
||||||
nokogiri (~> 1.6.0)
|
nokogiri (~> 1.6.0)
|
||||||
rails-deprecated_sanitizer (>= 1.0.1)
|
rails-deprecated_sanitizer (>= 1.0.1)
|
||||||
rails-erd (1.4.0)
|
rails-erd (1.4.2)
|
||||||
activerecord (>= 3.2)
|
activerecord (>= 3.2)
|
||||||
activesupport (>= 3.2)
|
activesupport (>= 3.2)
|
||||||
choice (~> 0.2.0)
|
choice (~> 0.2.0)
|
||||||
ruby-graphviz (~> 1.2)
|
ruby-graphviz (~> 1.2)
|
||||||
rails-html-sanitizer (1.0.2)
|
rails-html-sanitizer (1.0.2)
|
||||||
loofah (~> 2.0)
|
loofah (~> 2.0)
|
||||||
|
rails-jquery-autocomplete (1.0.3)
|
||||||
|
rails (>= 3.2)
|
||||||
railties (4.2.1)
|
railties (4.2.1)
|
||||||
actionpack (= 4.2.1)
|
actionpack (= 4.2.1)
|
||||||
activesupport (= 4.2.1)
|
activesupport (= 4.2.1)
|
||||||
|
@ -149,7 +145,7 @@ GEM
|
||||||
responders (2.1.0)
|
responders (2.1.0)
|
||||||
railties (>= 4.2.0, < 5)
|
railties (>= 4.2.0, < 5)
|
||||||
ruby-graphviz (1.2.2)
|
ruby-graphviz (1.2.2)
|
||||||
sass (3.4.14)
|
sass (3.4.16)
|
||||||
sass-rails (5.0.3)
|
sass-rails (5.0.3)
|
||||||
railties (>= 4.0.0, < 5.0)
|
railties (>= 4.0.0, < 5.0)
|
||||||
sass (~> 3.1)
|
sass (~> 3.1)
|
||||||
|
@ -172,7 +168,7 @@ GEM
|
||||||
spring (1.3.6)
|
spring (1.3.6)
|
||||||
sprockets (3.2.0)
|
sprockets (3.2.0)
|
||||||
rack (~> 1.0)
|
rack (~> 1.0)
|
||||||
sprockets-rails (2.3.1)
|
sprockets-rails (2.3.2)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
sprockets (>= 2.8, < 4.0)
|
sprockets (>= 2.8, < 4.0)
|
||||||
|
@ -182,6 +178,11 @@ GEM
|
||||||
tilt (1.4.1)
|
tilt (1.4.1)
|
||||||
turbolinks (2.5.3)
|
turbolinks (2.5.3)
|
||||||
coffee-rails
|
coffee-rails
|
||||||
|
twitter-bootstrap-rails (3.2.0)
|
||||||
|
actionpack (~> 4.1)
|
||||||
|
execjs (~> 2.2)
|
||||||
|
rails (~> 4.1)
|
||||||
|
railties (~> 4.1)
|
||||||
tzinfo (1.2.2)
|
tzinfo (1.2.2)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uglifier (2.7.1)
|
uglifier (2.7.1)
|
||||||
|
@ -189,7 +190,7 @@ GEM
|
||||||
json (>= 1.8.0)
|
json (>= 1.8.0)
|
||||||
warden (1.2.3)
|
warden (1.2.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
web-console (2.1.3)
|
web-console (2.2.1)
|
||||||
activemodel (>= 4.0)
|
activemodel (>= 4.0)
|
||||||
binding_of_caller (>= 0.7.2)
|
binding_of_caller (>= 0.7.2)
|
||||||
railties (>= 4.0)
|
railties (>= 4.0)
|
||||||
|
@ -200,28 +201,31 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
bootstrap-sass
|
acts_as_commentable
|
||||||
bootstrap-will_paginate
|
bootstrap-will_paginate
|
||||||
byebug
|
byebug
|
||||||
carrierwave
|
carrierwave
|
||||||
coffee-rails (~> 4.1.0)
|
coffee-rails (~> 4.1.0)
|
||||||
devise
|
devise
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
erd
|
|
||||||
jbuilder (~> 2.0)
|
jbuilder (~> 2.0)
|
||||||
jquery-rails
|
jquery-rails
|
||||||
|
jquery-ui-rails
|
||||||
pg
|
pg
|
||||||
pry
|
pry
|
||||||
puma
|
puma
|
||||||
rails (= 4.2.1)
|
rails (= 4.2.1)
|
||||||
|
rails-erd
|
||||||
|
rails-jquery-autocomplete
|
||||||
sass-rails (~> 5.0)
|
sass-rails (~> 5.0)
|
||||||
sdoc (~> 0.4.0)
|
sdoc (~> 0.4.0)
|
||||||
slim-rails
|
slim-rails
|
||||||
spring
|
spring
|
||||||
turbolinks
|
turbolinks
|
||||||
|
twitter-bootstrap-rails
|
||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
web-console (~> 2.0)
|
web-console (~> 2.0)
|
||||||
will_paginate
|
will_paginate
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.10.3
|
1.11.2
|
||||||
|
|
675
License
Normal file
675
License
Normal file
|
@ -0,0 +1,675 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
{one line to give the program's name and a brief idea of what it does.}
|
||||||
|
Copyright (C) 2015 {name of author}
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
MorningPeak Copyright (C) 2015 Arthur Poulet
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
|
|
138
README.md
138
README.md
|
@ -1,51 +1,133 @@
|
||||||
# Installation
|
# Project Presentation
|
||||||
|
|
||||||
## Requirements
|
_Morning Peak is a modern and OpenSource Web Application.
|
||||||
|
It has been designed to manage clients, bills, and issues for little and medium compagnies._
|
||||||
|
|
||||||
- First install ruby 2 or greater. It is not advised to do the following commands as ``root``.
|
<img att="Dashboard Mobile view" src="http://imgur.com/bnYHYJ6l.png" width="200" />
|
||||||
- Then, be sure you have postgresql installed, and started.
|
<img alt="Dashboard Desktop view" src="http://i.imgur.com/oxE9LR1.png" width="500" />
|
||||||
|
|
||||||
## Initialization
|
There is a lot of CRM on the web. Some of them are web application. Some of them are open source. Some of them are modern.
|
||||||
|
But, I __never see any of them grouping the 3__. It is why I started to develop this application.
|
||||||
|
To improve my skills, my knowledges, and to use it.
|
||||||
|
|
||||||
|
I want a simple application, that I __understand__, and that I __need__. By __simple__, I mean:
|
||||||
|
|
||||||
|
- No tasks, no reports, no chat. I want an application to do ONE thing, manage my clients, and it must do it clearly, and good.
|
||||||
|
- A pluggable application, with many __REST API__. Because tasks and reports are usefull.
|
||||||
|
- Generic items. I prefere __ONE kind of Bills + Tags__ than __4 ou 5 kinds of Bills__ (for example).
|
||||||
|
|
||||||
|
<img alt="Tickets Desktop view" src="http://i.imgur.com/e004zBZ.png" width="500" />
|
||||||
|
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
1. [Installation](#1-installation)
|
||||||
|
1. [Requirements](#11-requirements)
|
||||||
|
2. [Initialization](#12-initialization)
|
||||||
|
3. [Configuration](#13-configuration)
|
||||||
|
4. [First start](#14-first-start)
|
||||||
|
2. [Contributions](#2-contributions)
|
||||||
|
1. [Contributors](#21-contributors)
|
||||||
|
2. [How to contribute](#22-how-to-contribute-)
|
||||||
|
3. [License](#23-license)
|
||||||
|
3. [Architecture](#3-architecture)
|
||||||
|
1. [Modelisation](#31-modelisation)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 1. Installation
|
||||||
|
|
||||||
|
## 1.1. Requirements
|
||||||
|
- Ruby 2.0 or greater.
|
||||||
|
- Postgresql server 9 or greater running with creditentials.
|
||||||
|
|
||||||
|
## 1.2. Initialization
|
||||||
|
Start by pasting this script in your shell:
|
||||||
```bash
|
```bash
|
||||||
cd Appli/
|
cd MorningPeak/
|
||||||
gem install bundler
|
gem install bundler
|
||||||
bundle install
|
bundle install
|
||||||
cp config/database.yml.example config/database.yml
|
cp config/database.yml.example config/database.yml
|
||||||
edit config/database.yml # Configure your database connection first
|
edit config/database.yml # Configure your database connection first
|
||||||
rake db:create # create the db
|
rake db:create
|
||||||
rake db:migrate # migrate the db
|
rake db:migrate
|
||||||
|
rake db:seed # will generate default data. Not on production ;)
|
||||||
```
|
```
|
||||||
|
|
||||||
you can try with an first example of dataset :
|
## 1.3. Configuration
|
||||||
|
- You can create a file ``.env`` to save your locals cvars without pollute your global env.
|
||||||
```
|
- You can also create ``.env.production`` etc. for environement specifics cvars
|
||||||
rake db:seed
|
- A ``.env`` file looks like:
|
||||||
|
```text
|
||||||
|
COMPANY: "Yolo production"
|
||||||
|
LOCALE: fr
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running
|
- The application's specifics cvars are :
|
||||||
|
- COMPANY
|
||||||
You can run the server by :
|
|
||||||
|
|
||||||
|
## 1.4. First start
|
||||||
|
When you done with the configuration of the database (editing ``config/database.yml``),
|
||||||
|
you can run the server by the following command :
|
||||||
```
|
```
|
||||||
rails s
|
rails s # "-b 0.0.0.0 -p 80" to test on internet :)
|
||||||
```
|
```
|
||||||
|
|
||||||
The server will be accessible via [localhost](http://localhost:3000)
|
The server will be accessible via [localhost:3000](http://localhost:3000)
|
||||||
|
|
||||||
# Organisation MVC
|
Default creditentials are generated in the seed:
|
||||||
|
|
||||||
Guide documentation : [visit](http://guides.rubyonrails.org/)
|
```text
|
||||||
|
user> login: user@client.com , password: user2015
|
||||||
|
```
|
||||||
|
|
||||||
The RoR Project works with some main parts :
|
```text
|
||||||
|
admin> login: admin@admin.admin , password: wir2015
|
||||||
|
```
|
||||||
|
|
||||||
- __/config/routes.rb__ : the routing. The url accessible from http://... will call a Controller.method
|
Checkout for [rails minidoc](RailsMinidoc.md) for a resume of rails.
|
||||||
- __/app/controllers/*__ : the controllers. they act from the parameters from the calling url
|
|
||||||
- __/app/models/*__ : the modelisation of the database
|
|
||||||
- __/app/views/*/*__ : the views rendered after the controller
|
|
||||||
- __/db/migrate/*__ : the migrations. They represents the state of the databases
|
|
||||||
- __/app/assets/__ : all stylesheets css, javascript, images
|
|
||||||
|
|
||||||
# Contributors
|
|
||||||
|
|
||||||
- poulet_a (as lead)
|
# 2. Contributions
|
||||||
|
|
||||||
|
## 2.1. Contributors
|
||||||
|
- __Arthur Poulet__ : main developper, upstream (poulet_a)
|
||||||
|
<img alt="Look at this cute face" src="https://pbs.twimg.com/media/CJ_ErJ2W8AAdev3.jpg" width="200" height="150" />
|
||||||
|
|
||||||
|
## 2.2. How to contribute ?
|
||||||
|
You can contribute to this project by Merge Request on the gitlab repository [here](https://gitlab.com/poulet_a/MorningPeak).
|
||||||
|
The best pratices are to create short commits, and short Merge Requests. Respect the git commit nomage convention as possible with:
|
||||||
|
|
||||||
|
- title
|
||||||
|
- description
|
||||||
|
- concerned issues with "#id"
|
||||||
|
|
||||||
|
We accept any kind of work : translations, bug fix, additionnal features, optimizations, documentation, etc.
|
||||||
|
|
||||||
|
Don't be afraid !
|
||||||
|
|
||||||
|
## 2.3. License
|
||||||
|
|
||||||
|
see the license in the ``License`` file.
|
||||||
|
|
||||||
|
It is under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
```
|
||||||
|
|
||||||
|
# 3. Architecture
|
||||||
|
|
||||||
|
## 3.1 Modelisation
|
||||||
|
|
||||||
|
### Creation and belongs
|
||||||
|
- User has a Client (which is created when the user is created)
|
||||||
|
- Client has many Contacts and Bills
|
||||||
|
- User/Admin has many Tickets and Comments
|
||||||
|
|
||||||
|
### Permissions
|
||||||
|
- Admin can Comment, Read, Edit, Delete, and Create everything
|
||||||
|
- User can open Ticket, Anwser and Close them.
|
||||||
|
- User can Read every Bills wich is associated to him
|
||||||
|
- User can not read his client informations (like notes, ...)
|
||||||
|
|
28
README.rdoc
28
README.rdoc
|
@ -1,28 +0,0 @@
|
||||||
== README
|
|
||||||
|
|
||||||
This README would normally document whatever steps are necessary to get the
|
|
||||||
application up and running.
|
|
||||||
|
|
||||||
Things you may want to cover:
|
|
||||||
|
|
||||||
* Ruby version
|
|
||||||
|
|
||||||
* System dependencies
|
|
||||||
|
|
||||||
* Configuration
|
|
||||||
|
|
||||||
* Database creation
|
|
||||||
|
|
||||||
* Database initialization
|
|
||||||
|
|
||||||
* How to run the test suite
|
|
||||||
|
|
||||||
* Services (job queues, cache servers, search engines, etc.)
|
|
||||||
|
|
||||||
* Deployment instructions
|
|
||||||
|
|
||||||
* ...
|
|
||||||
|
|
||||||
|
|
||||||
Please feel free to use a different markup language if you do not plan to run
|
|
||||||
<tt>rake doc:app</tt>.
|
|
22
RailsMinidoc.md
Normal file
22
RailsMinidoc.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Rails minidoc
|
||||||
|
|
||||||
|
Guide documentation : [visit](http://guides.rubyonrails.org/)
|
||||||
|
|
||||||
|
The RoR Project works with some main parts :
|
||||||
|
|
||||||
|
- __/config/routes.rb__ : the routing. The url accessible from http://... will call a Controller.method
|
||||||
|
- __/app/controllers/*__ : the controllers. they are the mind using the parameters and choosing the render to use
|
||||||
|
- __/app/models/*__ : the modelisation of the database
|
||||||
|
- __/app/views/*/*__ : the views rendered after the controller
|
||||||
|
- __/db/migrate/*__ : the migrations. They represents the state of the databases
|
||||||
|
- __/app/assets/__ : all stylesheets css, javascript, images
|
||||||
|
|
||||||
|
## Create a new table, upgrade, etc.
|
||||||
|
|
||||||
|
### Create a table
|
||||||
|
|
||||||
|
Creating a new table = Create a new migration. Use the command like ``rails generate Table data1:string data2:integer`` etc.
|
||||||
|
|
||||||
|
### Change an existing table
|
||||||
|
|
||||||
|
Each changement on the database needs a migration. ``rails g migration``.
|
BIN
app/assets/images/icone_bill.png
Normal file
BIN
app/assets/images/icone_bill.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
app/assets/images/icone_client.png
Normal file
BIN
app/assets/images/icone_client.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
app/assets/images/icone_contact.png
Normal file
BIN
app/assets/images/icone_contact.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
app/assets/images/icone_export.png
Normal file
BIN
app/assets/images/icone_export.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
app/assets/images/icone_ticket.png
Normal file
BIN
app/assets/images/icone_ticket.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
|
@ -12,5 +12,8 @@
|
||||||
//
|
//
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery_ujs
|
//= require jquery_ujs
|
||||||
//= require turbolinks
|
//= require jquery-ui
|
||||||
|
//= require twitter/bootstrap
|
||||||
|
//= require jquery-ui/autocomplete
|
||||||
|
//= require autocomplete-rails
|
||||||
//= require_tree .
|
//= require_tree .
|
||||||
|
|
3
app/assets/javascripts/bootstrap.js.coffee
Normal file
3
app/assets/javascripts/bootstrap.js.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
jQuery ->
|
||||||
|
$("a[rel~=popover], .has-popover").popover()
|
||||||
|
$("a[rel~=tooltip], .has-tooltip").tooltip()
|
11
app/assets/javascripts/commentable.coffee
Normal file
11
app/assets/javascripts/commentable.coffee
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
commentableLoadComments = (base_url) ->
|
||||||
|
$.get(base_url + "/comments/about").done (e) ->
|
||||||
|
$("#comments").html(e)
|
||||||
|
|
||||||
|
$(document).ready ->
|
||||||
|
url = window.location.pathname
|
||||||
|
commentableLoadComments(url)
|
||||||
|
$(document).on "ajax:success", "#new_comment", (e, data, status, xhr) ->
|
||||||
|
commentableLoadComments(url)
|
||||||
|
$(document).on "ajax:complete", ".remove-comment", (e, data, status, xhr) ->
|
||||||
|
commentableLoadComments(url)
|
|
@ -1,3 +1,10 @@
|
||||||
# Place all the behaviors and hooks related to the matching controller here.
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
# All this logic will automatically be available in application.js.
|
# All this logic will automatically be available in application.js.
|
||||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||||
|
updateViewAtWhenClick = () ->
|
||||||
|
$('.contact-link-view_at').bind('ajax:success', (event, data, status, xhr) ->
|
||||||
|
this.parentElement.children[7].textContent = data["view_at"]
|
||||||
|
)
|
||||||
|
|
||||||
|
$ ->
|
||||||
|
updateViewAtWhenClick()
|
||||||
|
|
3
app/assets/javascripts/date.coffee
Normal file
3
app/assets/javascripts/date.coffee
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
$ ->
|
||||||
|
# $("#contact_view_at").datepicker()
|
||||||
|
$(".form-date").datepicker({ dateFormat: 'yy-mm-dd' })
|
17
app/assets/stylesheets/application.css
Normal file
17
app/assets/stylesheets/application.css
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||||
|
* compiled file so the styles you add here take precedence over styles defined in any styles
|
||||||
|
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
||||||
|
* file per style scope.
|
||||||
|
*
|
||||||
|
*= require jquery-ui
|
||||||
|
*= require_tree .
|
||||||
|
*= require_self
|
||||||
|
*= require "dashboard"
|
||||||
|
*/
|
|
@ -1,2 +0,0 @@
|
||||||
@import 'bootstrap-sprockets'
|
|
||||||
@import 'bootstrap'
|
|
7
app/assets/stylesheets/bootstrap_and_overrides.css
vendored
Normal file
7
app/assets/stylesheets/bootstrap_and_overrides.css
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
=require twitter-bootstrap-static/bootstrap
|
||||||
|
|
||||||
|
Use Font Awesome icons (default)
|
||||||
|
To use Glyphicons sprites instead of Font Awesome, replace with "require twitter-bootstrap-static/sprites"
|
||||||
|
=require twitter-bootstrap-static/fontawesome
|
||||||
|
*/
|
105
app/assets/stylesheets/dashboard.scss
Normal file
105
app/assets/stylesheets/dashboard.scss
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Base structure
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Move down content because we have a fixed navbar that is 50px tall */
|
||||||
|
body {
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global add-ons
|
||||||
|
*/
|
||||||
|
|
||||||
|
.sub-header {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Top navigation
|
||||||
|
* Hide default border to remove 1px line.
|
||||||
|
*/
|
||||||
|
.navbar-fixed-top {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sidebar
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Hide for mobile, show later */
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 51px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: block;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-right: 1px solid #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar navigation */
|
||||||
|
.nav-sidebar {
|
||||||
|
margin-right: -21px; /* 20px padding + 1px border */
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: -20px;
|
||||||
|
}
|
||||||
|
.nav-sidebar > li > a {
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.nav-sidebar > .active > a,
|
||||||
|
.nav-sidebar > .active > a:hover,
|
||||||
|
.nav-sidebar > .active > a:focus {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #428bca;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main content
|
||||||
|
*/
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.main {
|
||||||
|
padding-right: 40px;
|
||||||
|
padding-left: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.main .page-header {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Placeholder dashboard ideas
|
||||||
|
*/
|
||||||
|
|
||||||
|
.placeholders {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.placeholders h4 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.placeholder {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.placeholder img {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
|
@ -18,16 +18,16 @@ pre {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
/* a { */
|
||||||
color: #000;
|
/* color: #000; */
|
||||||
&:visited {
|
/* &:visited { */
|
||||||
color: #666;
|
/* color: #666; */
|
||||||
}
|
/* } */
|
||||||
&:hover {
|
/* &:hover { */
|
||||||
color: #fff;
|
/* color: #fff; */
|
||||||
background-color: #000;
|
/* background-color: #000; */
|
||||||
}
|
/* } */
|
||||||
}
|
/* } */
|
||||||
|
|
||||||
div {
|
div {
|
||||||
&.field, &.actions {
|
&.field, &.actions {
|
||||||
|
@ -35,9 +35,9 @@ div {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#notice {
|
/* #notice { */
|
||||||
color: green;
|
/* color: green; */
|
||||||
}
|
/* } */
|
||||||
|
|
||||||
.field_with_errors {
|
.field_with_errors {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
// Place all the styles related to the tickets controller here.
|
.ticket-description {
|
||||||
// They will automatically be included in application.css.
|
border: 1px dashed black;
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-creator {
|
||||||
|
color: #d9534f;
|
||||||
|
}
|
|
@ -1,13 +1,10 @@
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
# Prevent CSRF attacks by raising an exception.
|
# Prevent CSRF attacks by raising an exception.
|
||||||
# For APIs, you may want to use :null_session instead.
|
# For APIs, you may want to use :null_session instead.
|
||||||
|
|
||||||
protect_from_forgery with: :exception
|
protect_from_forgery with: :exception
|
||||||
|
|
||||||
# before_action :authenticate_user!
|
# before_action :authenticate_user!
|
||||||
# before_action :authenticate_admin!
|
# before_action :authenticate_admin!
|
||||||
|
|
||||||
# def admin_page!
|
|
||||||
# redirect_to root_url, notice: 'You must be admin to access to this page' if current_user.nil? or not current_user.admin?
|
|
||||||
# end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
class BillsController < ApplicationController
|
class BillsController < ApplicationController
|
||||||
|
include CommentableForm
|
||||||
|
|
||||||
|
autocomplete :client, :name
|
||||||
|
|
||||||
before_action :set_bill, only: [:show, :edit, :update, :destroy]
|
before_action :set_bill, only: [:show, :edit, :update, :destroy]
|
||||||
before_action :authenticate_admin!
|
before_action :authenticate_admin!
|
||||||
|
|
||||||
|
@ -11,6 +15,7 @@ class BillsController < ApplicationController
|
||||||
# GET /bills/1
|
# GET /bills/1
|
||||||
# GET /bills/1.json
|
# GET /bills/1.json
|
||||||
def show
|
def show
|
||||||
|
prepare_comment_for @bill
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /bills/new
|
# GET /bills/new
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
class ClientTicketsController < ApplicationController
|
class ClientTicketsController < ApplicationController
|
||||||
|
include CommentableForm
|
||||||
|
|
||||||
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
|
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
|
||||||
before_action :set_ticket_custom_route, only: [:close, :open, :respond]
|
before_action :set_ticket_custom_route, only: [:close, :open, :respond]
|
||||||
|
before_action :looks_ticket, only: [:show, :edit, :update, :close, :open]
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
|
||||||
# GET /tickets
|
# GET /tickets
|
||||||
# GET /tickets.json
|
# GET /tickets.json
|
||||||
def index
|
def index
|
||||||
@tickets = current_user.tickets.heads.order('state DESC')
|
@tickets = current_user.tickets.order('state DESC')
|
||||||
end
|
end
|
||||||
|
|
||||||
def close
|
def close
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.close
|
if @ticket.close
|
||||||
format.html { redirect_to tickets_url, notice: 'Ticket was successfully closed.' }
|
@ticket.update(admin_view_at: Time.now) # add comment ?
|
||||||
|
format.html { redirect_to client_ticket_url(@ticket), notice: 'Ticket was successfully closed.' }
|
||||||
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
||||||
else
|
else
|
||||||
format.html { render :index }
|
format.html { redirect_to client_ticket_url(@ticket), alert: 'The ticket is already close' }
|
||||||
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -24,33 +28,20 @@ class ClientTicketsController < ApplicationController
|
||||||
def open
|
def open
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.open
|
if @ticket.open
|
||||||
format.html { redirect_to tickets_url, notice: 'Ticket was successfully reopened.' }
|
@ticket.update(admin_view_at: nil) # add comment ?
|
||||||
|
format.html { redirect_to client_ticket_url(@ticket), notice: 'Ticket was successfully reopened.' }
|
||||||
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
||||||
else
|
else
|
||||||
format.html { render :index }
|
format.html { redirect_to client_ticket_url(@ticket), alert: 'The ticket is already open' }
|
||||||
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond
|
|
||||||
session[:user_ticket_respond_parent_id] = @ticket.id
|
|
||||||
@ticket = Ticket.new(ticket_id: @ticket.head.id,
|
|
||||||
creator: current_user,
|
|
||||||
title: @ticket.title)
|
|
||||||
if @ticket.close? or @ticket.head.close?
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to tickets_url }
|
|
||||||
format.json { render json: {ticket: 'the ticket is closed'}, status: :unprocessable_entity }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /tickets/1
|
# GET /tickets/1
|
||||||
# GET /tickets/1.json
|
# GET /tickets/1.json
|
||||||
def show
|
def show
|
||||||
|
prepare_comment_for @ticket
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /tickets/new
|
# GET /tickets/new
|
||||||
|
@ -66,13 +57,11 @@ class ClientTicketsController < ApplicationController
|
||||||
# POST /tickets.json
|
# POST /tickets.json
|
||||||
def create
|
def create
|
||||||
@ticket = Ticket.new(ticket_params)
|
@ticket = Ticket.new(ticket_params)
|
||||||
@ticket.ticket_id = session[:user_ticket_respond_parent_id]
|
|
||||||
@ticket.creator = current_user
|
@ticket.creator = current_user
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.save
|
if @ticket.save
|
||||||
@ticket.close_head if @ticket.close?
|
format.html { redirect_to client_ticket_url(@ticket), notice: 'Ticket was successfully created.' }
|
||||||
format.html { redirect_to @ticket, notice: 'Ticket was successfully created.' }
|
|
||||||
format.json { render :show, status: :created, location: client_tickets_url(@ticket) }
|
format.json { render :show, status: :created, location: client_tickets_url(@ticket) }
|
||||||
else
|
else
|
||||||
format.html { render :new }
|
format.html { render :new }
|
||||||
|
@ -86,7 +75,6 @@ class ClientTicketsController < ApplicationController
|
||||||
def update
|
def update
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.update(ticket_params)
|
if @ticket.update(ticket_params)
|
||||||
@ticket.close_head if @ticket.close?
|
|
||||||
format.html { redirect_to @ticket, notice: 'Ticket was successfully updated.' }
|
format.html { redirect_to @ticket, notice: 'Ticket was successfully updated.' }
|
||||||
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
||||||
else
|
else
|
||||||
|
@ -109,11 +97,11 @@ class ClientTicketsController < ApplicationController
|
||||||
private
|
private
|
||||||
# Use callbacks to share common setup or constraints between actions.
|
# Use callbacks to share common setup or constraints between actions.
|
||||||
def set_ticket
|
def set_ticket
|
||||||
@ticket = Ticket.find(params[:id])
|
@ticket = Ticket.find_by(id: params[:id], creator: current_user)
|
||||||
@ticket = nil if not @ticket.creator == current_user and not @ticket.head.creator == current_user
|
|
||||||
@ticket
|
@ticket
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: remove
|
||||||
def set_ticket_custom_route
|
def set_ticket_custom_route
|
||||||
params[:id] = params[:client_ticket_id]
|
params[:id] = params[:client_ticket_id]
|
||||||
set_ticket
|
set_ticket
|
||||||
|
@ -124,4 +112,8 @@ class ClientTicketsController < ApplicationController
|
||||||
params.require(:ticket).permit(:title, :description, :state)
|
params.require(:ticket).permit(:title, :description, :state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def looks_ticket
|
||||||
|
@ticket.set_view_by('User')
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
class ClientsController < ApplicationController
|
class ClientsController < ApplicationController
|
||||||
before_action :set_client, only: [:show, :edit, :update, :destroy]
|
include CommentableForm
|
||||||
|
|
||||||
|
autocomplete :contact, :name
|
||||||
|
|
||||||
before_action :authenticate_admin!
|
before_action :authenticate_admin!
|
||||||
|
before_action :set_client, only: [:show, :edit, :update, :destroy]
|
||||||
|
|
||||||
# GET /clients
|
# GET /clients
|
||||||
# GET /clients.json
|
# GET /clients.json
|
||||||
|
@ -11,6 +15,7 @@ class ClientsController < ApplicationController
|
||||||
# GET /clients/1
|
# GET /clients/1
|
||||||
# GET /clients/1.json
|
# GET /clients/1.json
|
||||||
def show
|
def show
|
||||||
|
prepare_comment_for @client
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /clients/new
|
# GET /clients/new
|
||||||
|
|
117
app/controllers/comments_controller.rb
Normal file
117
app/controllers/comments_controller.rb
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
class CommentsController < ApplicationController
|
||||||
|
before_action :set_comment, only: [:show, :edit, :update, :destroy]
|
||||||
|
before_action :set_commentable, only: [:index, :about, :show, :create]
|
||||||
|
before_action :set_commentable_client, only: [:about_client, :show, :create]
|
||||||
|
|
||||||
|
# GET /comments
|
||||||
|
# GET /comments.json
|
||||||
|
def index
|
||||||
|
@comments = Comment.where(commentable: @commentable) if @commentable
|
||||||
|
@comments ||= Comment.where('0=1')
|
||||||
|
end
|
||||||
|
|
||||||
|
def about
|
||||||
|
index()
|
||||||
|
@comment = Comment.new
|
||||||
|
@comment.commentable = @commentable
|
||||||
|
render :about, layout: false
|
||||||
|
end
|
||||||
|
def about_client
|
||||||
|
index()
|
||||||
|
@comment = Comment.new
|
||||||
|
@comment.commentable = @commentable
|
||||||
|
render :about_client, layout: false
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /comments/1
|
||||||
|
# GET /comments/1.json
|
||||||
|
def show
|
||||||
|
binding.pry
|
||||||
|
render status: :forbidden unless admin_signed_in? or (user_signed_in? and @comment.commentable.is_a? Ticket and @comment.commentable.creator == current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /comments/new
|
||||||
|
def new
|
||||||
|
@comment = Comment.new
|
||||||
|
@comment.commentable = @commentable
|
||||||
|
render status: :forbidden unless admin_signed_in? or (user_signed_in? and @comment.commentable.is_a? Ticket and @comment.commentable.creator == current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /comments/1/edit
|
||||||
|
def edit
|
||||||
|
render status: :forbidden unless admin_signed_in? or (user_signed_in? and @comment.commentable.is_a? Ticket and @comment.commentable.creator == current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /comments
|
||||||
|
# POST /comments.json
|
||||||
|
def create
|
||||||
|
@comment = Comment.new(comment_params)
|
||||||
|
@comment.creator = (current_admin || current_user)
|
||||||
|
render status: :forbidden unless admin_signed_in? or (user_signed_in? and @comment.commentable.is_a? Ticket and @comment.commentable.creator == current_user)
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
if @comment.save
|
||||||
|
format.html { redirect_to @comment, notice: 'Comment was successfully created.' }
|
||||||
|
format.json { render :show, status: :created, location: @comment }
|
||||||
|
else
|
||||||
|
format.html { render :new }
|
||||||
|
format.json { render json: @comment.errors, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PATCH/PUT /comments/1
|
||||||
|
# PATCH/PUT /comments/1.json
|
||||||
|
def update
|
||||||
|
render status: :forbidden unless admin_signed_in?
|
||||||
|
respond_to do |format|
|
||||||
|
if @comment.update(comment_params)
|
||||||
|
format.html { redirect_to @comment, notice: 'Comment was successfully updated.' }
|
||||||
|
format.json { render :show, status: :ok, location: @comment }
|
||||||
|
else
|
||||||
|
format.html { render :edit }
|
||||||
|
format.json { render json: @comment.errors, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /comments/1
|
||||||
|
# DELETE /comments/1.json
|
||||||
|
def destroy
|
||||||
|
render status: :forbidden unless admin_signed_in?
|
||||||
|
@comment.destroy
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
|
||||||
|
format.json { head :no_content }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Use callbacks to share common setup or constraints between actions.
|
||||||
|
def set_comment
|
||||||
|
@comment = Comment.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Never trust parameters from the scary internet, only allow the white list through.
|
||||||
|
def comment_params
|
||||||
|
params.require(:comment).permit(:title, :comment, :commentable_id, :commentable_type,
|
||||||
|
#:creator_id, :creator_type,
|
||||||
|
:role)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_commentable
|
||||||
|
return unless admin_signed_in?
|
||||||
|
@commentable ||= Client.find_by_id(params[:client_id])
|
||||||
|
@commentable ||= Contact.find_by_id(params[:contact_id])
|
||||||
|
@commentable ||= Bill.find_by_id(params[:bill_id])
|
||||||
|
@commentable ||= Ticket.find_by_id(params[:ticket_id])
|
||||||
|
@commentable_by = 'Admin'
|
||||||
|
end
|
||||||
|
def set_commentable_client
|
||||||
|
return unless user_signed_in?
|
||||||
|
if @commentable.nil?
|
||||||
|
@commentable ||= Ticket.find_by(id: params[:client_ticket_id], creator: current_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
8
app/controllers/concerns/commentable_form.rb
Normal file
8
app/controllers/concerns/commentable_form.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module CommentableForm
|
||||||
|
|
||||||
|
def prepare_comment_for(commentable)
|
||||||
|
@comment = Comment.new
|
||||||
|
@comment.commentable = commentable
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,7 +1,17 @@
|
||||||
class ContactsController < ApplicationController
|
class ContactsController < ApplicationController
|
||||||
before_action :set_contact, only: [:show, :edit, :update, :destroy]
|
include CommentableForm
|
||||||
|
|
||||||
|
autocomplete :client, :name
|
||||||
|
|
||||||
|
before_action :set_contact, only: [:show, :edit, :update, :destroy, :view]
|
||||||
before_action :authenticate_admin!
|
before_action :authenticate_admin!
|
||||||
|
|
||||||
|
# PATCH /view/1
|
||||||
|
def view
|
||||||
|
@contact.update(view_at: Time.now)
|
||||||
|
render json: @contact, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
# GET /contacts
|
# GET /contacts
|
||||||
# GET /contacts.json
|
# GET /contacts.json
|
||||||
def index
|
def index
|
||||||
|
@ -11,6 +21,7 @@ class ContactsController < ApplicationController
|
||||||
# GET /contacts/1
|
# GET /contacts/1
|
||||||
# GET /contacts/1.json
|
# GET /contacts/1.json
|
||||||
def show
|
def show
|
||||||
|
prepare_comment_for @contact
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /contacts/new
|
# GET /contacts/new
|
||||||
|
@ -70,6 +81,6 @@ class ContactsController < ApplicationController
|
||||||
|
|
||||||
# Never trust parameters from the scary internet, only allow the white list through.
|
# Never trust parameters from the scary internet, only allow the white list through.
|
||||||
def contact_params
|
def contact_params
|
||||||
params.require(:contact).permit(:client_id, :name, :phone, :email, :last_contact, :note, :region, :department, :postal_code, :address)
|
params.require(:contact).permit(:client_id, :name, :phone, :email, :note, :region, :department, :postal_code, :address)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,37 @@
|
||||||
class HomeController < ApplicationController
|
class HomeController < ApplicationController
|
||||||
|
before_action :authenticate_admin!, only: [:admin_dashboard, :export]
|
||||||
|
before_action :authenticate_user!, only: [:user_dashboard]
|
||||||
|
|
||||||
def index
|
def user_dashboard
|
||||||
|
@retard_bills = current_user.bills.retard.count
|
||||||
|
@advanced_bills = current_user.bills.advanced.count
|
||||||
|
@next_bills = current_user.bills.next.count
|
||||||
|
@old_bills = current_user.bills.old.count
|
||||||
|
@bills_count = current_user.bills.count
|
||||||
|
@ticket_closed = current_user.tickets.close.count
|
||||||
|
@ticket_opened = current_user.tickets.open.count
|
||||||
|
@ticket_waiting = current_user.tickets_unview.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_dashboard
|
||||||
|
@clients_count = Client.count
|
||||||
|
@contacts_count = Contact.count
|
||||||
|
@no_client_contacts_count = (Client.all.pluck(:id) - Contact.all.pluck(:client_id).uniq).size
|
||||||
|
@retard_bills = Bill.retard.count
|
||||||
|
@advanced_bills = Bill.advanced.count
|
||||||
|
@next_bills = Bill.next.count
|
||||||
|
@old_bills = Bill.old.count
|
||||||
|
@bills_count = Bill.count
|
||||||
|
@ticket_closed = Ticket.close.count
|
||||||
|
@ticket_opened = Ticket.open.count
|
||||||
|
@ticket_waiting = current_admin.tickets_unview.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def export
|
||||||
|
redirect_to "/admins/export.json" if params[:format] != "json"
|
||||||
|
@clients = Client.all
|
||||||
|
@contacts = Contact.all
|
||||||
|
@bills = Bill.all
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
class TicketsController < ApplicationController
|
class TicketsController < ApplicationController
|
||||||
|
include CommentableForm
|
||||||
|
|
||||||
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
|
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
|
||||||
before_action :set_ticket_custom_route, only: [:close, :open, :respond]
|
before_action :set_ticket_custom_route, only: [:close, :open]
|
||||||
|
before_action :looks_ticket, only: [:show, :edit, :update, :close, :open]
|
||||||
before_action :authenticate_admin!
|
before_action :authenticate_admin!
|
||||||
|
|
||||||
# GET /tickets
|
# GET /tickets
|
||||||
# GET /tickets.json
|
# GET /tickets.json
|
||||||
def index
|
def index
|
||||||
@tickets = Ticket.heads.order('state DESC')
|
@tickets = Ticket.order('state DESC')
|
||||||
end
|
end
|
||||||
|
|
||||||
def close
|
def close
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.close
|
if @ticket.close
|
||||||
format.html { redirect_to tickets_url, notice: 'Ticket was successfully closed.' }
|
@ticket.update(admin_view_at: Time.now) # add comment ?
|
||||||
|
format.html { redirect_to ticket_url(@ticket), notice: 'Ticket was successfully closed.' }
|
||||||
format.json { render :show, status: :ok, location: @ticket }
|
format.json { render :show, status: :ok, location: @ticket }
|
||||||
else
|
else
|
||||||
format.html { render :index }
|
format.html { redirect_to ticket_url(@ticket), alert: 'The ticket is already close' }
|
||||||
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -24,32 +28,20 @@ class TicketsController < ApplicationController
|
||||||
def open
|
def open
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.open
|
if @ticket.open
|
||||||
format.html { redirect_to tickets_url, notice: 'Ticket was successfully reopened.' }
|
@ticket.update(creator_view_at: nil) # add comment ?
|
||||||
|
format.html { redirect_to ticket_url(@ticket), notice: 'Ticket was successfully reopened.' }
|
||||||
format.json { render :show, status: :ok, location: @ticket }
|
format.json { render :show, status: :ok, location: @ticket }
|
||||||
else
|
else
|
||||||
format.html { render :index }
|
format.html { redirect_to ticket_url(@ticket), alert: 'The ticket is already open' }
|
||||||
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
format.json { render json: @ticket.errors, status: :unprocessable_entity }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond
|
|
||||||
@ticket = Ticket.new(ticket_id: @ticket.head.id,
|
|
||||||
creator: current_admin,
|
|
||||||
title: @ticket.title)
|
|
||||||
if @ticket.close? or @ticket.head.close?
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to tickets_url }
|
|
||||||
format.json { render json: {ticket: 'the ticket is closed'}, status: :unprocessable_entity }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
render :new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /tickets/1
|
# GET /tickets/1
|
||||||
# GET /tickets/1.json
|
# GET /tickets/1.json
|
||||||
def show
|
def show
|
||||||
|
prepare_comment_for @ticket
|
||||||
end
|
end
|
||||||
|
|
||||||
# GET /tickets/new
|
# GET /tickets/new
|
||||||
|
@ -84,7 +76,6 @@ class TicketsController < ApplicationController
|
||||||
def update
|
def update
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @ticket.update(ticket_params)
|
if @ticket.update(ticket_params)
|
||||||
@ticket.close_head if @ticket.close?
|
|
||||||
format.html { redirect_to @ticket, notice: 'Ticket was successfully updated.' }
|
format.html { redirect_to @ticket, notice: 'Ticket was successfully updated.' }
|
||||||
format.json { render :show, status: :ok, location: @ticket }
|
format.json { render :show, status: :ok, location: @ticket }
|
||||||
else
|
else
|
||||||
|
@ -116,7 +107,11 @@ class TicketsController < ApplicationController
|
||||||
|
|
||||||
# Never trust parameters from the scary internet, only allow the white list through.
|
# Never trust parameters from the scary internet, only allow the white list through.
|
||||||
def ticket_params
|
def ticket_params
|
||||||
params.require(:ticket).permit(:ticket_id, :title, :description, :state)
|
params.require(:ticket).permit(:title, :description, :state)
|
||||||
|
end
|
||||||
|
|
||||||
|
def looks_ticket
|
||||||
|
@ticket.set_view_by('Admin')
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
|
||||||
def application_title
|
def application_title
|
||||||
['Appli', ENV['COMPANY'], 'client gestion'].join(' ')
|
['MorningPeak', ENV['COMPANY'], 'CRM'].compact.join(' ')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
module BillsHelper
|
module BillsHelper
|
||||||
|
|
||||||
|
def user_bills_count_due
|
||||||
|
return nil if not user_signed_in?
|
||||||
|
return (current_user.bills.next + current_user.bills.retard).count
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
2
app/helpers/comments_helper.rb
Normal file
2
app/helpers/comments_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module CommentsHelper
|
||||||
|
end
|
|
@ -1,2 +1,8 @@
|
||||||
module TicketClientsHelper
|
module TicketClientsHelper
|
||||||
|
|
||||||
|
def user_tickets_unview_count
|
||||||
|
return nil if not user_signed_in?
|
||||||
|
return current_user.tickets_unview.count
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,4 +8,39 @@ module TicketsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def admin_tickets_unview_count
|
||||||
|
return if not admin_signed_in?
|
||||||
|
return current_admin.tickets_unview.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def brize text
|
||||||
|
texts = text.split("\n")
|
||||||
|
texts.join(tag(:br))
|
||||||
|
end
|
||||||
|
|
||||||
|
def imagize text, uris
|
||||||
|
uris.each do |uri|
|
||||||
|
text.gsub!(uri, "<a href='#{uri}'><img src='#{uri}' alt='#{uri}'/></a>")
|
||||||
|
end
|
||||||
|
text
|
||||||
|
end
|
||||||
|
|
||||||
|
def linkize text, uris
|
||||||
|
uris.each do |uri|
|
||||||
|
text.gsub!(uri, "<a href='#{uri}'>#{uri}</a>")
|
||||||
|
end
|
||||||
|
text
|
||||||
|
end
|
||||||
|
|
||||||
|
def descriptionize text
|
||||||
|
uris = URI.extract(text)
|
||||||
|
imgs = uris.select{|e| e.match(/\.(jpg|png|jpeg|gif)\Z/)}
|
||||||
|
links = uris - imgs
|
||||||
|
text = raw text
|
||||||
|
text = brize text
|
||||||
|
text = imagize text, imgs
|
||||||
|
text = linkize text, links
|
||||||
|
text.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,5 +9,10 @@ class Admin < ActiveRecord::Base
|
||||||
#:validatable
|
#:validatable
|
||||||
|
|
||||||
has_many :tickets, as: :creator
|
has_many :tickets, as: :creator
|
||||||
|
has_many :comments, as: :creator
|
||||||
|
|
||||||
|
def tickets_unview
|
||||||
|
Ticket.all.where(admin_view_at: nil)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
class Bill < ActiveRecord::Base
|
class Bill < ActiveRecord::Base
|
||||||
|
acts_as_commentable
|
||||||
belongs_to :client
|
belongs_to :client
|
||||||
|
|
||||||
|
validates :due_date, presence: true
|
||||||
|
|
||||||
delegate :name, to: :client, prefix: true
|
delegate :name, to: :client, prefix: true
|
||||||
|
|
||||||
|
scope :retard, -> {where("due_date <= '#{Date.today}'").where(paid: false)}
|
||||||
|
scope :advanced, -> {where("due_date > '#{Date.today}'").where(paid: true)}
|
||||||
|
scope :next, -> {where("due_date > '#{Date.today}'").where(paid: false)}
|
||||||
|
scope :old, -> {where("due_date <= '#{Date.today}'").where(paid: true)}
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
class Client < ActiveRecord::Base
|
class Client < ActiveRecord::Base
|
||||||
|
acts_as_commentable
|
||||||
|
|
||||||
has_many :contacts
|
has_many :contacts
|
||||||
has_many :bills
|
has_many :bills
|
||||||
has_many :tickets, through: :user
|
has_many :tickets, through: :user
|
||||||
|
@ -8,7 +10,7 @@ class Client < ActiveRecord::Base
|
||||||
delegate :name, to: :contact, allow_nil: true, prefix: true
|
delegate :name, to: :contact, allow_nil: true, prefix: true
|
||||||
|
|
||||||
def last_contact
|
def last_contact
|
||||||
contacts.pluck('last_contact').max || "never"
|
contacts.where('view_at IS NOT NULL').pluck(:view_at).max || "never"
|
||||||
end
|
end
|
||||||
|
|
||||||
def bills_retard
|
def bills_retard
|
||||||
|
|
31
app/models/comment.rb
Normal file
31
app/models/comment.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
class Comment < ActiveRecord::Base
|
||||||
|
include ActsAsCommentable::Comment
|
||||||
|
|
||||||
|
belongs_to :commentable, :polymorphic => true
|
||||||
|
|
||||||
|
default_scope -> { order('created_at DESC') }
|
||||||
|
|
||||||
|
# NOTE: install the acts_as_votable plugin if you
|
||||||
|
# want user to vote on the quality of comments.
|
||||||
|
#acts_as_voteable
|
||||||
|
|
||||||
|
# NOTE: Comments belong to a creator
|
||||||
|
belongs_to :creator, polymorphic: true
|
||||||
|
|
||||||
|
def thread(commentable=:self)
|
||||||
|
commentable = self.commentable if commentable == :self
|
||||||
|
if commentable.nil?
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
Comment.where(commentable: commentable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after_create :update_view_date_for_ticket
|
||||||
|
def update_view_date_for_ticket
|
||||||
|
if commentable.is_a? Ticket
|
||||||
|
commentable.updated_by(creator.class.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,4 +1,5 @@
|
||||||
class Contact < ActiveRecord::Base
|
class Contact < ActiveRecord::Base
|
||||||
|
acts_as_commentable
|
||||||
belongs_to :client
|
belongs_to :client
|
||||||
|
|
||||||
delegate :name, to: :client, prefix: true
|
delegate :name, to: :client, prefix: true
|
||||||
|
|
|
@ -1,58 +1,83 @@
|
||||||
|
# coding: utf-8
|
||||||
class Ticket < ActiveRecord::Base
|
class Ticket < ActiveRecord::Base
|
||||||
|
acts_as_commentable
|
||||||
|
|
||||||
|
# WARNING ! IT REVERSE LAST / FIRST
|
||||||
|
default_scope { order('created_at DESC') }
|
||||||
|
|
||||||
OPEN = 'open'
|
OPEN = 'open'
|
||||||
CLOSE = 'close'
|
CLOSE = 'close'
|
||||||
|
|
||||||
CREATORS = ['Admin', 'User']
|
CREATORS = ['Admin', 'User']
|
||||||
|
|
||||||
|
validates :state, inclusion: {in: [OPEN, CLOSE]}
|
||||||
|
# validates :creator, inclusion: {in: CREATORS}
|
||||||
|
validates :title, length: {in: 7..124}
|
||||||
|
validates :description, length: {minimum: 12}
|
||||||
|
|
||||||
belongs_to :creator, polymorphic: true
|
belongs_to :creator, polymorphic: true
|
||||||
delegate :name, to: :creator, prefix: true
|
delegate :name, to: :creator, prefix: true
|
||||||
|
|
||||||
belongs_to :ticket
|
scope :open, -> { where(state: OPEN) }
|
||||||
has_many :tickets
|
scope :close, -> { where(state: CLOSE) }
|
||||||
|
|
||||||
scope :heads, -> { where(ticket_id: nil) }
|
|
||||||
scope :open, -> { heads.where(state: OPEN) }
|
|
||||||
scope :close, -> { heads.where(state: CLOSE) }
|
|
||||||
|
|
||||||
before_save :check_creator
|
before_save :check_creator
|
||||||
def check_creator
|
def check_creator
|
||||||
return true if CREATORS.include? creator_type
|
return true if CREATORS.include? creator_type
|
||||||
errors.add "#{creator_type} is not in the list of the possible creators list"
|
errors[:base] << "#{creator_type} is not in the list of the possible creators list"
|
||||||
raise ActiveRecord::RecordInvalid.new(self)
|
raise ActiveRecord::RecordInvalid.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_response
|
after_create :set_view_init
|
||||||
head.tickets.last
|
def set_view_init
|
||||||
|
set_view_by(creator_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def head
|
def set_view_by(viewer_type)
|
||||||
return self if not ticket
|
if creator_type == 'Admin'
|
||||||
return ticket.head
|
if viewer_type == 'Admin'
|
||||||
|
update(admin_view_at: Time.now, creator_view_at: Time.now)
|
||||||
|
end
|
||||||
|
elsif creator_type == 'User'
|
||||||
|
if viewer_type == 'User'
|
||||||
|
update(creator_view_at: Time.now)
|
||||||
|
elsif viewer_type == 'Admin'
|
||||||
|
update(admin_view_at: Time.now)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_unview_by(viewer_type)
|
||||||
|
if creator_type == 'Admin'
|
||||||
|
return # ... ?
|
||||||
|
elsif creator_type == 'User'
|
||||||
|
if viewer_type == 'User'
|
||||||
|
update(admin_view_at: nil)
|
||||||
|
elsif viewer_type == 'Admin'
|
||||||
|
update(creator_view_at: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def updated_by(viewer_type)
|
||||||
|
set_view_by(viewer_type)
|
||||||
|
set_unview_by(viewer_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def close
|
def close
|
||||||
if close?
|
if close?
|
||||||
errors.add "Already close"
|
errors[:base] << "Already close"
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
head.update(state: Ticket::CLOSE) && update(state: Ticket::CLOSE)
|
update(state: Ticket::CLOSE)
|
||||||
end
|
|
||||||
|
|
||||||
def close_head
|
|
||||||
if head.close?
|
|
||||||
errors.add "Already close"
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
head.update(state: Ticket::CLOSE)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def open
|
def open
|
||||||
if open?
|
if open?
|
||||||
errors.add "Already open"
|
errors[:base] << "Already open"
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
head.update(state: Ticket::OPEN) && update(state: Ticket::OPEN)
|
update(state: Ticket::OPEN)
|
||||||
end
|
end
|
||||||
|
|
||||||
def open?
|
def open?
|
||||||
|
@ -63,4 +88,8 @@ class Ticket < ActiveRecord::Base
|
||||||
state == CLOSE
|
state == CLOSE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def short_description
|
||||||
|
description.to_s.first(100)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,12 +10,19 @@ class User < ActiveRecord::Base
|
||||||
has_one :client
|
has_one :client
|
||||||
delegate :name, to: :client
|
delegate :name, to: :client
|
||||||
has_many :tickets, as: :creator
|
has_many :tickets, as: :creator
|
||||||
|
has_many :comments, as: :creator
|
||||||
has_many :contacts, through: :client
|
has_many :contacts, through: :client
|
||||||
has_many :bills, through: :client
|
has_many :bills, through: :client
|
||||||
|
|
||||||
after_create :create_client
|
after_create :create_client
|
||||||
def create_client
|
def create_client
|
||||||
update(client: Client.create(name: email.gsub(/@.+/, '')))
|
if self.client_id.nil?
|
||||||
|
update_attributes(client: Client.create(name: email.tr('@.-_', ' ')))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def tickets_unview
|
||||||
|
tickets.where(creator_view_at: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,25 +6,38 @@
|
||||||
- @bill.errors.full_messages.each do |message|
|
- @bill.errors.full_messages.each do |message|
|
||||||
li = message
|
li = message
|
||||||
|
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :client_id
|
= f.autocomplete_field :client, autocomplete_client_name_bills_path, :id_element => '#bill_client_id', class: "form-control", placeholder: "Client Name"
|
||||||
= f.text_field :client_id
|
= f.label :client_id, class: "sr-only"
|
||||||
|
= f.text_field :client_id, class: "form-control sr-only", placeholder: "#{t('.client_id')}"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :title
|
= f.label :title, class: "sr-only"
|
||||||
= f.text_field :title
|
= f.text_field :title, class: "form-control", placeholder: "#{t('.title')}"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :description
|
= f.label :description, class: "sr-only"
|
||||||
= f.text_area :description
|
= f.text_area :description, class: "form-control", placeholder: "#{t('.description')}"
|
||||||
|
.form-inline
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :amount
|
= f.label :amount, class: "sr-only"
|
||||||
= f.number_field :amount
|
= f.number_field :amount, class: "form-control", placeholder: "#{t('.amount')}"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :paid
|
= f.label t('.is_paid')
|
||||||
= f.check_box :paid
|
br
|
||||||
|
= f.check_box :paid, class: "form-control", placeholder: "#{t('.is_paid')}"
|
||||||
|
.form-inline
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :emission_date
|
= f.label :emission_date, class: "sr-only"
|
||||||
= f.date_select :emission_date
|
= f.text_field :emission_date, class: "form-control form-date", placeholder: "#{t('.e_date')}"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :due_date
|
= f.label :due_date, class: "sr-only"
|
||||||
= f.date_select :due_date
|
= f.text_field :due_date, class: "form-control form-date", placeholder: "#{t('.d_date')}"
|
||||||
.actions = f.submit
|
|
||||||
|
.actions = f.submit class: "btn btn-success"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
h1 Editing bill
|
h1.page-header = t(".page.title")
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
= link_to 'Show', @bill, class: 'btn btn-sm btn-default'
|
= link_to t("show"), @bill, class: 'btn btn-sm btn-default'
|
||||||
= link_to 'Back', bills_path, class: 'btn btn-sm btn-default'
|
= link_to t("back"), bills_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
h1 Listing bills
|
h1.page-header = t(".page.title")
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
table.table.table-condensed
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
td ID
|
th = t(".id")
|
||||||
th Client
|
th = t(".client")
|
||||||
th Title
|
th = t(".title")
|
||||||
th Description
|
th = t(".description")
|
||||||
th Amount
|
th = t(".amount")
|
||||||
th Paid
|
th = t(".paid")
|
||||||
th Emission date
|
th = t(".e_date")
|
||||||
th Due date
|
th = t(".d_date")
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
|
@ -32,10 +32,10 @@ table.table.table-condensed.table-striped
|
||||||
td.bg-danger = distance_of_time_in_words_to_now(bill.due_date) + " - " + bill.due_date.to_s(:long)
|
td.bg-danger = distance_of_time_in_words_to_now(bill.due_date) + " - " + bill.due_date.to_s(:long)
|
||||||
- else
|
- else
|
||||||
td.bg-info = distance_of_time_in_words_to_now(bill.due_date).to_s + " - " + bill.due_date.to_s(:long)
|
td.bg-info = distance_of_time_in_words_to_now(bill.due_date).to_s + " - " + bill.due_date.to_s(:long)
|
||||||
td = link_to '', bill, class: 'glyphicon glyphicon-eye-open'
|
td = link_to t('show'), bill
|
||||||
td = link_to '', edit_bill_path(bill), class: 'glyphicon glyphicon-pencil'
|
td = link_to t('edit'), edit_bill_path(bill)
|
||||||
td = link_to '', bill, data: {:confirm => 'Are you sure?'}, :method => :delete, class: 'glyphicon glyphicon-remove-sign'
|
td = link_to t('destroy'), bill, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
= link_to 'New Bill', new_bill_path, class: 'btn btn-sm btn-default'
|
= link_to t("new"), new_bill_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
h1 New bill
|
h1.page-header = t(".page.title")
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
= link_to 'Back', bills_path
|
= link_to t("back"), bills_path
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
p#notice = notice
|
h1.page-header = "#{t('.page.title')}: #{@bill.title}"
|
||||||
|
|
||||||
p
|
p
|
||||||
strong Client:
|
strong = t ".client"
|
||||||
= link_to @bill.client.name, @bill.client
|
= link_to @bill.client.name, @bill.client
|
||||||
p
|
p
|
||||||
strong Title:
|
strong = t ".title"
|
||||||
= @bill.title
|
= @bill.title
|
||||||
p
|
p
|
||||||
strong Description:
|
strong = t ".description"
|
||||||
= @bill.description
|
= @bill.description
|
||||||
p
|
p
|
||||||
strong Amount:
|
strong = t ".amount"
|
||||||
= @bill.amount
|
= @bill.amount
|
||||||
p
|
p
|
||||||
strong Paid:
|
strong = t ".is_paid"
|
||||||
= @bill.paid
|
= @bill.paid
|
||||||
p
|
p
|
||||||
strong Emission date:
|
strong = t ".e_date"
|
||||||
= @bill.emission_date
|
= @bill.emission_date
|
||||||
p
|
p
|
||||||
strong Due date:
|
strong = t ".d_date"
|
||||||
- if @bill.due_date < Date.today
|
- if @bill.due_date < Date.today
|
||||||
- if @bill.paid
|
- if @bill.paid
|
||||||
.bg-success = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
.bg-success = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
||||||
|
@ -28,5 +28,10 @@ p
|
||||||
- else
|
- else
|
||||||
.bg-info = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
.bg-info = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
||||||
|
|
||||||
= link_to 'Edit', edit_bill_path(@bill), class: 'btn btn-sm btn-default'
|
= link_to t('edit'), edit_bill_path(@bill), class: 'btn btn-sm btn-default'
|
||||||
= link_to 'Back', bills_path, class: 'btn btn-sm btn-default'
|
= link_to t('back'), bills_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
||||||
|
#comments.container-fluid
|
||||||
|
h2.page-header Comments
|
||||||
|
= render partial: "/comments/about", locals: {commentable: @bill}
|
||||||
|
= render partial: "/comments/form"
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
h1 Listing bills
|
h1.page-header = t '.page.title'
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
table.table.table-condensed
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
td ID
|
th ID
|
||||||
th Title
|
th = t '.title'
|
||||||
th Description
|
th = t '.description'
|
||||||
th Amount
|
th = t '.amount'
|
||||||
th Paid
|
th = t '.paid'
|
||||||
th Emission date
|
th = t '.emission_date'
|
||||||
th Due date
|
th = t '.due_date'
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
|
@ -30,6 +30,6 @@ table.table.table-condensed.table-striped
|
||||||
td.bg-danger = distance_of_time_in_words_to_now(bill.due_date) + " - " + bill.due_date.to_s(:long)
|
td.bg-danger = distance_of_time_in_words_to_now(bill.due_date) + " - " + bill.due_date.to_s(:long)
|
||||||
- else
|
- else
|
||||||
td.bg-info = distance_of_time_in_words_to_now(bill.due_date).to_s + " - " + bill.due_date.to_s(:long)
|
td.bg-info = distance_of_time_in_words_to_now(bill.due_date).to_s + " - " + bill.due_date.to_s(:long)
|
||||||
td = link_to '', bill, class: 'glyphicon glyphicon-eye-open'
|
td = link_to t('show'), client_bill_url(bill)
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
p#notice = notice
|
h1.page-header = t '.page.title'
|
||||||
|
|
||||||
p
|
p
|
||||||
strong Title:
|
strong = t ".title"
|
||||||
= @bill.title
|
= @bill.title
|
||||||
p
|
p
|
||||||
strong Description:
|
strong = t ".description"
|
||||||
= @bill.description
|
= @bill.description
|
||||||
p
|
p
|
||||||
strong Amount:
|
strong = t ".amount"
|
||||||
= @bill.amount
|
= @bill.amount
|
||||||
p
|
p
|
||||||
strong Paid:
|
strong = t ".paid"
|
||||||
= @bill.paid
|
= @bill.paid
|
||||||
p
|
p
|
||||||
strong Emission date:
|
strong = t ".emission_date"
|
||||||
= @bill.emission_date
|
= @bill.emission_date
|
||||||
p
|
p
|
||||||
strong Due date:
|
strong = t ".due_date"
|
||||||
- if @bill.due_date < Date.today
|
- if @bill.due_date < Date.today
|
||||||
- if @bill.paid
|
- if @bill.paid
|
||||||
.bg-success = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
.bg-success = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
= form_for @ticket, url: client_tickets_path do |f|
|
= form_for @ticket, url: @ticket.id ? client_ticket_path(@ticket) : client_tickets_path do |f|
|
||||||
- if @ticket.errors.any?
|
- if @ticket.errors.any?
|
||||||
#error_explanation
|
#error_explanation
|
||||||
h2 = "#{pluralize(@ticket.errors.count, "error")} prohibited this ticket from being saved:"
|
h2 = "#{pluralize(@ticket.errors.count, "error")} prohibited this ticket from being saved:"
|
||||||
|
@ -6,13 +6,18 @@
|
||||||
- @ticket.errors.full_messages.each do |message|
|
- @ticket.errors.full_messages.each do |message|
|
||||||
li = message
|
li = message
|
||||||
|
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :title
|
= f.label :title, class: "sr-only"
|
||||||
= f.text_field :title
|
= f.text_field :title, class: "form-control", placeholder: "#{t('.title')}"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :description
|
= f.label :description, class: "sr-only"
|
||||||
= f.text_area :description
|
= f.text_area :description, class: "form-control", size: "100x8", placeholder: "#{t('.description')}"
|
||||||
|
/ display the choice to close only if 'respond to'
|
||||||
|
- if @head
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :state
|
= f.label t('.state')
|
||||||
= f.text_field :state
|
= f.select :state, ["open", "close"], {}, class: "form-control"
|
||||||
.actions = f.submit
|
.actions = f.submit class: "btn btn-default"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
h1 Editing ticket
|
h1.page-header = t(".page.title")
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
= link_to 'Show', client_ticket_path(@ticket), class: 'btn btn-sm btn-default'
|
= link_to t('show'), client_ticket_path(@ticket), class: 'btn btn-sm btn-default'
|
||||||
= link_to 'Back', client_tickets_path, class: 'btn btn-sm btn-default'
|
= link_to t('back'), client_tickets_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
|
@ -1,42 +1,37 @@
|
||||||
h1 Listing tickets
|
h1.page-header = t ".page.title"
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
table.table.table-condensed
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
td ID
|
th ID
|
||||||
th Creator
|
th = t ".title"
|
||||||
th Title
|
th = t ".description"
|
||||||
th Description
|
th = t ".state"
|
||||||
th State
|
th = t ".last_update"
|
||||||
th Last update
|
th = t ".last_response"
|
||||||
th Last response
|
|
||||||
th
|
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
|
|
||||||
tbody
|
tbody
|
||||||
- @tickets.each do |ticket|
|
- @tickets.each do |ticket|
|
||||||
tr
|
tr class="#{ticket.creator_view_at.nil? ? 'bg-warning' : ''}"
|
||||||
td = link_to("##{ticket.id}", client_ticket_path(ticket))
|
td = link_to("##{ticket.id}", client_ticket_path(ticket))
|
||||||
td = link_to_creator(ticket)
|
|
||||||
td = link_to ticket.title, client_ticket_path(ticket)
|
td = link_to ticket.title, client_ticket_path(ticket)
|
||||||
td = ticket.description
|
td = ticket.short_description
|
||||||
- if ticket.state == Ticket::CLOSE
|
- if ticket.state == Ticket::CLOSE
|
||||||
td.bg-danger = ticket.state
|
td.bg-danger = t ".states.close"
|
||||||
- else
|
- else
|
||||||
td.bg-success = ticket.state
|
td.bg-success = t ".states.open"
|
||||||
td = ticket.updated_at.to_s(:long)
|
td = ticket.updated_at.to_s(:long)
|
||||||
td = ticket.last_response ? distance_of_time_in_words_to_now(ticket.last_response.updated_at) : "never"
|
td = ticket.comments.empty? ? "never" : distance_of_time_in_words_to_now(ticket.comments.first.updated_at)
|
||||||
td = link_to '', ticket, class: 'glyphicon glyphicon-eye-open'
|
td = link_to (t "show"), ticket
|
||||||
td = link_to '', edit_client_ticket_path(ticket), class: 'glyphicon glyphicon-pencil'
|
td = link_to (t "edit"), edit_client_ticket_path(ticket)
|
||||||
/ td = link_to '', ticket, data: {:confirm => 'Are you sure?'}, :method => :delete, class: 'glyphicon glyphicon-remove-sign'
|
|
||||||
td = link_to '', ticket_respond_path(ticket), class: 'glyphicon glyphicon-comment' if ticket.open?
|
|
||||||
- if ticket.open?
|
- if ticket.open?
|
||||||
td = link_to '', client_ticket_close_path(ticket), data: {:confirm => 'Are you sure to CLOSE this ticket?'}, method: :patch, class: 'glyphicon glyphicon-ok-circle'
|
td = link_to (t "close"), client_ticket_close_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||||
- elsif ticket.close?
|
- elsif ticket.close?
|
||||||
td = link_to '', client_ticket_open_path(ticket), data: {:confirm => 'Are you sure to REOPEN this ticket?'}, method: :patch, class: 'glyphicon glyphicon-remove-circle'
|
td = link_to (t "reopen"), client_ticket_open_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
= link_to 'New Ticket', new_client_ticket_path, class: 'btn btn-sm btn-default'
|
= link_to (t "new"), new_client_ticket_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
h1 New ticket
|
h1 = @head ? t("client_tickets.response.page.title") : t(".page.title")
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
= link_to 'Back', client_tickets_path
|
= link_to t('back'), client_tickets_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
|
@ -1,62 +1,44 @@
|
||||||
p#notice = notice
|
h1.page-header = "Ticket: #{@ticket.title}"
|
||||||
|
|
||||||
p
|
p
|
||||||
strong
|
strong
|
||||||
| Id: #
|
| Id: #
|
||||||
= @ticket.id
|
= @ticket.id
|
||||||
p
|
p
|
||||||
strong Réponse à:
|
strong = t ".title"
|
||||||
= link_to @ticket.head.title, client_ticket_path(@ticket.head)
|
|:
|
||||||
|
= link_to @ticket.title, client_ticket_path(@ticket)
|
||||||
|
|
||||||
p
|
p
|
||||||
strong State:
|
strong = t ".state"
|
||||||
- if @ticket.head.state == Ticket::CLOSE
|
|:
|
||||||
td.bg-danger = @ticket.head.state
|
- if @ticket.state == Ticket::CLOSE
|
||||||
|
td.bg-danger = t ".states.close"
|
||||||
- else
|
- else
|
||||||
td.bg-success = @ticket.head.state
|
td.bg-success = t ".states.open"
|
||||||
|
|
||||||
p
|
p
|
||||||
strong Creator:
|
strong = t ".creator"
|
||||||
= link_to_creator @ticket
|
|:
|
||||||
|
span.ticket-creator = link_to_creator @ticket
|
||||||
p
|
p
|
||||||
strong Title:
|
strong = t ".title"
|
||||||
|
|:
|
||||||
= @ticket.title
|
= @ticket.title
|
||||||
p
|
p
|
||||||
strong Description:
|
strong = t ".description"
|
||||||
= @ticket.description
|
|:
|
||||||
p
|
.container-fluid.ticket-description
|
||||||
strong State:
|
= descriptionize @ticket.description
|
||||||
= @ticket.state
|
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
== render partial: "/comments/thread", locals: {commentable: @ticket, type: 'user'}
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Creator
|
|
||||||
th Title
|
|
||||||
th Description
|
|
||||||
th Last update
|
|
||||||
th
|
|
||||||
th
|
|
||||||
/ th
|
|
||||||
|
|
||||||
tbody
|
|
||||||
- @ticket.tickets.each do |ticket|
|
|
||||||
tr
|
|
||||||
td = link_to_creator(ticket)
|
|
||||||
td = link_to ticket.title, client_ticket_path(ticket)
|
|
||||||
td = ticket.description
|
|
||||||
- if ticket.state == Ticket::CLOSE
|
|
||||||
td.bg-danger = ticket.state
|
|
||||||
- else
|
|
||||||
td.bg-success = ticket.state
|
|
||||||
td = ticket.updated_at.to_s(:long)
|
|
||||||
td = link_to 'Show', client_ticket_path(ticket)
|
|
||||||
td = link_to 'Edit', edit_client_ticket_path(ticket)
|
|
||||||
/ td = link_to 'Destroy', client_ticket_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :delete
|
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
|
- if @ticket.open?
|
||||||
|
= link_to (t 'close'), client_ticket_close_path(@ticket), method: :patch, class: 'btn btn-sm btn-success'
|
||||||
|
- else
|
||||||
|
= link_to (t 'reopen'), client_ticket_open_path(@ticket), method: :patch, class: 'btn btn-sm btn-danger'
|
||||||
|
= link_to (t 'edit'), edit_client_ticket_path(@ticket), class: 'btn btn-sm btn-default'
|
||||||
|
= link_to (t 'back'), client_tickets_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
||||||
= link_to 'Respond', client_ticket_respond_path(@ticket), class: 'btn btn-sm btn-default'
|
|
||||||
= link_to 'Edit', edit_client_ticket_path(@ticket), class: 'btn btn-sm btn-default'
|
|
||||||
= link_to 'Back', client_tickets_path, class: 'btn btn-sm btn-default'
|
|
||||||
|
|
|
@ -6,22 +6,31 @@
|
||||||
- @client.errors.full_messages.each do |message|
|
- @client.errors.full_messages.each do |message|
|
||||||
li = message
|
li = message
|
||||||
|
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :name
|
= f.label :name, class: "sr-only"
|
||||||
= f.text_field :name
|
= f.text_field :name, class: "form-control", placeholder: "Name"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :url
|
= f.label :url, class: "sr-only"
|
||||||
= f.text_field :url
|
= f.text_field :url, class: "form-control", placeholder: "URL"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :activity
|
= f.label :activity, class: "sr-only"
|
||||||
= f.text_field :activity
|
= f.text_field :activity, class: "form-control", placeholder: "Activity"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :contact_reasons
|
= f.label :contact_reasons, class: "sr-only"
|
||||||
= f.text_area :contact_reasons
|
= f.text_area :contact_reasons, class: "form-control", placeholder: "Contact reasons"
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :user_id
|
= f.label :user_id, class: "sr-only"
|
||||||
= f.text_field :user_id
|
= f.text_field :user_id, class: "form-control", placeholder: "User ID"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :contact_id
|
= f.autocomplete_field :contact, autocomplete_contact_name_clients_path, :id_element => '#client_contact_id', class: "form-control", placeholder: "Main Contact Name"
|
||||||
= f.text_field :contact_id
|
= f.label :contact_id, class: "sr-only"
|
||||||
.actions = f.submit
|
= f.text_field :contact_id, class: "form-control sr-only", placeholder: "Main Contact ID"
|
||||||
|
.actions = f.submit class: "btn btn-success"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
h1 Editing client
|
h1.page-header Editing client
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
h1 Listing clients
|
h1.page-header Listing clients
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
table.table.table-condensed
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
td ID
|
td ID
|
||||||
|
@ -32,9 +32,9 @@ table.table.table-condensed.table-striped
|
||||||
td = client.retard
|
td = client.retard
|
||||||
td = client.weight
|
td = client.weight
|
||||||
td = client.next
|
td = client.next
|
||||||
td = link_to '', client, class: 'glyphicon glyphicon-eye-open'
|
td = link_to 'Show', client
|
||||||
td = link_to '', edit_client_path(client), class: 'glyphicon glyphicon-pencil'
|
td = link_to 'Edit', edit_client_path(client)
|
||||||
td = link_to '', client, data: {:confirm => 'Are you sure?'}, :method => :delete, class: 'glyphicon glyphicon-remove-sign'
|
td = link_to 'Destroy', client, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
h1 New client
|
h1.page-header New client
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
p#notice = notice
|
h1.page-header Show Client
|
||||||
|
|
||||||
p
|
p
|
||||||
strong Name:
|
strong Name:
|
||||||
|
@ -29,8 +29,9 @@ p
|
||||||
= link_to 'Edit', edit_client_path(@client), class: 'btn btn-sm btn-default'
|
= link_to 'Edit', edit_client_path(@client), class: 'btn btn-sm btn-default'
|
||||||
= link_to 'Back', clients_path, class: 'btn btn-sm btn-default'
|
= link_to 'Back', clients_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
||||||
|
#contacts.container-fluid
|
||||||
|
h2 Contacts list
|
||||||
table.table.table-condensed
|
table.table.table-condensed
|
||||||
caption Contacts list
|
|
||||||
tr
|
tr
|
||||||
th Name
|
th Name
|
||||||
th Phone
|
th Phone
|
||||||
|
@ -41,6 +42,8 @@ table.table.table-condensed
|
||||||
td = contact.phone
|
td = contact.phone
|
||||||
td = contact.email
|
td = contact.email
|
||||||
|
|
||||||
|
#bills.container-fluid
|
||||||
|
h2 Bills list
|
||||||
table.table.table-condensed
|
table.table.table-condensed
|
||||||
caption = "Retarded Due Bills list #{@client.bills_retard.pluck(:amount).sum} €"
|
caption = "Retarded Due Bills list #{@client.bills_retard.pluck(:amount).sum} €"
|
||||||
tr
|
tr
|
||||||
|
@ -76,3 +79,8 @@ table.table.table-condensed
|
||||||
td.bg-success = link_to bill.title, bill
|
td.bg-success = link_to bill.title, bill
|
||||||
td.bg-success = bill.amount
|
td.bg-success = bill.amount
|
||||||
td.bg-success = bill.due_date
|
td.bg-success = bill.due_date
|
||||||
|
|
||||||
|
#comments.container-fluid
|
||||||
|
h2.page-header Comments
|
||||||
|
= render partial: "/comments/about", locals: {commentable: @client}
|
||||||
|
= render partial: "/comments/form"
|
||||||
|
|
14
app/views/comments/_about.html.slim
Normal file
14
app/views/comments/_about.html.slim
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
table.table.table-condensed
|
||||||
|
tr
|
||||||
|
th Creator
|
||||||
|
th Title
|
||||||
|
th Content
|
||||||
|
th
|
||||||
|
- commentable.comments.each do |c|
|
||||||
|
tr
|
||||||
|
td = c.creator_type
|
||||||
|
td = c.title
|
||||||
|
td = descriptionize c.comment
|
||||||
|
td
|
||||||
|
= link_to "/comments/#{c.id}.json", remote: true, data: {:confirm => 'Are you sure?'}, method: :delete, class: "close remove-comment", type: "button", "aria-label" => "Close" do
|
||||||
|
span aria-hidden="true" ×
|
10
app/views/comments/_about_client.html.slim
Normal file
10
app/views/comments/_about_client.html.slim
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
table.table.table-condensed
|
||||||
|
tr
|
||||||
|
th Creator
|
||||||
|
th Title
|
||||||
|
th Content
|
||||||
|
- commentable.comments.each do |c|
|
||||||
|
tr
|
||||||
|
td = c.creator_type
|
||||||
|
td = c.title
|
||||||
|
td = descriptionize c.comment
|
36
app/views/comments/_form.html.slim
Normal file
36
app/views/comments/_form.html.slim
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
= form_for @comment, format: :json, remote: true do |f|
|
||||||
|
- if @comment.errors.any?
|
||||||
|
#error_explanation
|
||||||
|
h2 = "#{pluralize(@comment.errors.count, "error")} prohibited this comment from being saved:"
|
||||||
|
ul
|
||||||
|
- @comment.errors.full_messages.each do |message|
|
||||||
|
li = message
|
||||||
|
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
|
.field
|
||||||
|
= f.label :title, class: "sr-only"
|
||||||
|
= f.text_field :title, class: "form-control", placeholder: "Title"
|
||||||
|
.form-group
|
||||||
|
.field
|
||||||
|
= f.label :comment, class: "sr-only"
|
||||||
|
= f.text_area :comment, class: "form-control", placeholder: "Content"
|
||||||
|
.form-inline
|
||||||
|
.field
|
||||||
|
= f.label :commentable_id, class: "form-control", class: "sr-only hidden"
|
||||||
|
= f.number_field :commentable_id, class: "form-control hidden", placeholder: "Commentable ID"
|
||||||
|
.field
|
||||||
|
= f.label :commentable_type, class: "sr-only hidden"
|
||||||
|
= f.text_field :commentable_type, class: "form-control hidden", placeholder: "Commentable Type"
|
||||||
|
.form-inline
|
||||||
|
.field
|
||||||
|
= f.label :creator_id, class: "sr-only"
|
||||||
|
= f.number_field :creator_id, class: "form-control hidden", placeholder: "Creator ID"
|
||||||
|
.field
|
||||||
|
= f.label :creator_type, class: "sr-only hidden"
|
||||||
|
= f.text_field :creator_type, class: "form-control hidden", placeholder: "Creator Type"
|
||||||
|
.form-inline
|
||||||
|
.field
|
||||||
|
= f.label :role, class: "sr-only hidden"
|
||||||
|
= f.text_field :role, class: "form-control hidden", placeholder: "Role"
|
||||||
|
.actions = f.submit class: "btn btn-success action-comment"
|
4
app/views/comments/_thread.html.slim
Normal file
4
app/views/comments/_thread.html.slim
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#comments.container-fluid
|
||||||
|
h2.page-header Comments
|
||||||
|
= render partial: "/comments/form"
|
||||||
|
= render partial: "/comments/about#{type == 'user' ? '_client' : '' }", locals: {commentable: commentable}
|
1
app/views/comments/about.html.slim
Normal file
1
app/views/comments/about.html.slim
Normal file
|
@ -0,0 +1 @@
|
||||||
|
== render partial: 'thread', locals: {commentable: @commentable, type: 'admin'}
|
1
app/views/comments/about_client.html.slim
Normal file
1
app/views/comments/about_client.html.slim
Normal file
|
@ -0,0 +1 @@
|
||||||
|
== render partial: 'thread', locals: {commentable: @commentable, type: 'user'}
|
7
app/views/comments/edit.html.slim
Normal file
7
app/views/comments/edit.html.slim
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
h1.page-header Editing comment
|
||||||
|
|
||||||
|
== render 'form'
|
||||||
|
|
||||||
|
= link_to t('show'), @comment
|
||||||
|
'|
|
||||||
|
= link_to t('back'), comments_path
|
33
app/views/comments/index.html.slim
Normal file
33
app/views/comments/index.html.slim
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
h1 Listing comments
|
||||||
|
|
||||||
|
table.table.table-condensed
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th Title
|
||||||
|
th Comment
|
||||||
|
th Commentable
|
||||||
|
th Commentable type
|
||||||
|
th Creator ID
|
||||||
|
th Creator Type
|
||||||
|
th Role
|
||||||
|
th
|
||||||
|
th
|
||||||
|
th
|
||||||
|
|
||||||
|
tbody
|
||||||
|
- @comments.each do |comment|
|
||||||
|
tr
|
||||||
|
td = comment.title
|
||||||
|
td = comment.comment
|
||||||
|
td = comment.commentable_id
|
||||||
|
td = comment.commentable_type
|
||||||
|
td = comment.creator_id
|
||||||
|
td = comment.creator_type
|
||||||
|
td = comment.role
|
||||||
|
td = link_to t('show'), comment
|
||||||
|
td = link_to t('edit'), edit_comment_path(comment)
|
||||||
|
td = link_to t('destroy'), comment, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||||
|
|
||||||
|
br
|
||||||
|
|
||||||
|
= link_to t('new'), new_comment_path
|
4
app/views/comments/index.json.jbuilder
Normal file
4
app/views/comments/index.json.jbuilder
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
json.array!(@comments) do |comment|
|
||||||
|
json.extract! comment, :id, :title, :comment, :commentable_id, :commentable_type, :creator_id, :creator_type, :role
|
||||||
|
json.url comment_url(comment, format: :json)
|
||||||
|
end
|
5
app/views/comments/new.html.slim
Normal file
5
app/views/comments/new.html.slim
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
h1.page-header New comment
|
||||||
|
|
||||||
|
== render 'form'
|
||||||
|
|
||||||
|
= link_to t('back'), comments_path
|
27
app/views/comments/show.html.slim
Normal file
27
app/views/comments/show.html.slim
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
p#notice = notice
|
||||||
|
|
||||||
|
p
|
||||||
|
strong Title:
|
||||||
|
= @comment.title
|
||||||
|
p
|
||||||
|
strong Comment:
|
||||||
|
= @comment.comment
|
||||||
|
p
|
||||||
|
strong Commentable:
|
||||||
|
= @comment.commentable_id
|
||||||
|
p
|
||||||
|
strong Commentable type:
|
||||||
|
= @comment.commentable_type
|
||||||
|
p
|
||||||
|
strong Creator id:
|
||||||
|
= @comment.creator_id
|
||||||
|
p
|
||||||
|
strong Creator type:
|
||||||
|
= @comment.creator_type
|
||||||
|
p
|
||||||
|
strong Role:
|
||||||
|
= @comment.role
|
||||||
|
|
||||||
|
= link_to t('edit'), edit_comment_path(@comment)
|
||||||
|
'|
|
||||||
|
= link_to t('back'), comments_path
|
1
app/views/comments/show.json.jbuilder
Normal file
1
app/views/comments/show.json.jbuilder
Normal file
|
@ -0,0 +1 @@
|
||||||
|
json.extract! @comment, :id, :title, :comment, :commentable_id, :commentable_type, :creator_id, :creator_type, :role, :created_at, :updated_at
|
|
@ -6,34 +6,49 @@
|
||||||
- @contact.errors.full_messages.each do |message|
|
- @contact.errors.full_messages.each do |message|
|
||||||
li = message
|
li = message
|
||||||
|
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :client_id
|
= f.autocomplete_field :client, autocomplete_client_name_contacts_path, :id_element => '#contact_client_id', class: "form-control", placeholder: "Client Name"
|
||||||
= f.text_field :client_id
|
= f.label :client_id, class: "sr-only"
|
||||||
|
= f.text_field :client_id, class: "form-control sr-only", placeholder: "Client ID"
|
||||||
|
.form-inline
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :name
|
= f.label :name, class: "sr-only"
|
||||||
= f.text_field :name
|
= f.text_field :name, class: "form-control", placeholder: "Name"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :phone
|
= f.label :phone, class: "sr-only"
|
||||||
= f.text_field :phone
|
= f.text_field :phone, class: "form-control", placeholder: "Phone"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :email
|
= f.label :email, class: "sr-only"
|
||||||
= f.text_field :email
|
= f.text_field :email, class: "form-control", placeholder: "Email"
|
||||||
|
.form-inline
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :last_contact
|
= f.label :region, class: "sr-only"
|
||||||
= f.date_select :last_contact
|
= f.text_field :region, class: "form-control", placeholder: "Region"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :note
|
= f.label :department, class: "sr-only"
|
||||||
= f.text_area :note
|
= f.text_field :department, class: "form-control", placeholder: "Department"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :region
|
= f.label :postal_code, class: "sr-only"
|
||||||
= f.text_field :region
|
= f.text_field :postal_code, class: "form-control", placeholder: "Postal Code"
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :department
|
= f.label :address, class: "sr-only"
|
||||||
= f.text_field :department
|
= f.text_area :address, class: "form-control", placeholder: "Address"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :postal_code
|
= f.label :note, class: "sr-only"
|
||||||
= f.text_field :postal_code
|
= f.text_area :note, class: "form-control", placeholder: "Note"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :address
|
= f.label :view_at, class: "sr-only"
|
||||||
= f.text_area :address
|
= f.text_field :view_at, class: "form-control form-date", placeholder: "View at"
|
||||||
.actions = f.submit
|
.actions = f.submit class: "btn btn-success"
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
h1 Editing contact
|
h1.page-header Editing contact
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
= link_to 'Show', @contact
|
= link_to 'Show', @contact
|
||||||
'|
|
'|
|
||||||
= link_to 'Back', contacts_path
|
= link_to 'Back', contacts_path
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
h1 Listing contacts
|
h1.page-header Listing contacts
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
table.table.table-condensed.
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
td ID
|
td ID
|
||||||
|
@ -8,12 +8,10 @@ table.table.table-condensed.table-striped
|
||||||
th Name
|
th Name
|
||||||
th Phone
|
th Phone
|
||||||
th Email
|
th Email
|
||||||
th Last contact
|
|
||||||
th Note
|
th Note
|
||||||
th Region
|
|
||||||
th Department
|
|
||||||
th Postal code
|
th Postal code
|
||||||
th Address
|
th Last view
|
||||||
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
|
@ -26,15 +24,15 @@ table.table.table-condensed.table-striped
|
||||||
td = link_to contact.name, contact
|
td = link_to contact.name, contact
|
||||||
td = contact.phone
|
td = contact.phone
|
||||||
td = contact.email
|
td = contact.email
|
||||||
td = contact.last_contact
|
|
||||||
td = contact.note
|
td = contact.note
|
||||||
td = contact.region
|
|
||||||
td = contact.department
|
|
||||||
td = contact.postal_code
|
td = contact.postal_code
|
||||||
td = contact.address
|
td.contact-value-view_at
|
||||||
td = link_to '', contact, class: 'glyphicon glyphicon-eye-open'
|
= contact.view_at
|
||||||
td = link_to '', edit_contact_path(contact), class: 'glyphicon glyphicon-pencil'
|
td.contact-link-view_at
|
||||||
td = link_to '', contact, data: {:confirm => 'Are you sure?'}, :method => :delete, class: 'glyphicon glyphicon-remove-sign'
|
= link_to 'View today!', view_contact_path(contact), method: :patch, remote: true
|
||||||
|
td = link_to 'Show', contact
|
||||||
|
td = link_to 'Edit', edit_contact_path(contact)
|
||||||
|
td = link_to 'Destroy', contact, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
json.array!(@contacts) do |contact|
|
json.array!(@contacts) do |contact|
|
||||||
json.extract! contact, :id, :client_id, :name, :phone, :email, :last_contact, :note, :region, :department, :postal_code, :address
|
json.extract! contact, :id, :client_id, :name, :phone, :email, :view_at, :note, :region, :department, :postal_code, :address
|
||||||
json.url contact_url(contact, format: :json)
|
json.url contact_url(contact, format: :json)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
h1 New contact
|
h1.page-header New contact
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
p#notice = notice
|
h1.page-header Show contact
|
||||||
|
|
||||||
p
|
p
|
||||||
strong Client:
|
strong Client:
|
||||||
|
@ -13,8 +13,8 @@ p
|
||||||
strong Email:
|
strong Email:
|
||||||
= @contact.email
|
= @contact.email
|
||||||
p
|
p
|
||||||
strong Last contact:
|
strong View at:
|
||||||
= @contact.last_contact
|
= @contact.view_at
|
||||||
p
|
p
|
||||||
strong Note:
|
strong Note:
|
||||||
= @contact.note
|
= @contact.note
|
||||||
|
@ -33,3 +33,8 @@ p
|
||||||
|
|
||||||
= link_to 'Edit', edit_contact_path(@contact), class: 'btn btn-sm btn-default'
|
= link_to 'Edit', edit_contact_path(@contact), class: 'btn btn-sm btn-default'
|
||||||
= link_to 'Back', contacts_path, class: 'btn btn-sm btn-default'
|
= link_to 'Back', contacts_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
||||||
|
#comments.container-fluid
|
||||||
|
h2.page-header Comments
|
||||||
|
= render partial: "/comments/about", locals: {commentable: @contact}
|
||||||
|
= render partial: "/comments/form"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
json.extract! @contact, :id, :client_id, :name, :phone, :email, :last_contact, :note, :region, :department, :postal_code, :address, :created_at, :updated_at
|
json.extract! @contact, :id, :client_id, :name, :phone, :email, :view_at, :note, :region, :department, :postal_code, :address, :created_at, :updated_at
|
||||||
|
|
58
app/views/home/admin_dashboard.html.slim
Normal file
58
app/views/home/admin_dashboard.html.slim
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
h1.page-header = t ".title"
|
||||||
|
|
||||||
|
.row.placeholders
|
||||||
|
.col-xs-6.col-sm-2.placeholder
|
||||||
|
= link_to clients_url do
|
||||||
|
= image_tag "icone_client.png", alt: "image clients", class: "img-responsive", size: "128x128"
|
||||||
|
h4 Clients
|
||||||
|
span.text-muted = "Count: #{@clients_count}"
|
||||||
|
br
|
||||||
|
|
||||||
|
.col-xs-6.col-sm-2.placeholder
|
||||||
|
= link_to contacts_url do
|
||||||
|
= image_tag "icone_contact.png", alt: "image contact", class: "img-responsive", size: "128x128"
|
||||||
|
h4 Contacts
|
||||||
|
span.text-muted = "Count: #{@contacts_count}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Client with no contacts: #{@no_client_contacts_count}"
|
||||||
|
br
|
||||||
|
|
||||||
|
.col-xs-6.col-sm-2.placeholder
|
||||||
|
= link_to bills_url do
|
||||||
|
= image_tag "icone_bill.png", alt: "image bills", class: "img-responsive", size: "128x128"
|
||||||
|
h4 Bills
|
||||||
|
span.text-muted = "Retard bills: #{@retard_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Advanced bills: #{@advanced_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Next bills: #{@retard_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Old bills: #{@old_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Total Bills: #{@bills_count}"
|
||||||
|
br
|
||||||
|
|
||||||
|
.col-xs-6.col-sm-2.placeholder
|
||||||
|
= link_to tickets_url do
|
||||||
|
= image_tag "icone_ticket.png", alt: "image tickets", class: "img-responsive", size: "128x128"
|
||||||
|
h4 Tickets
|
||||||
|
span.text-muted = "Closed tickets: #{@ticket_closed}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Opened tickets: #{@ticket_opened}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Waiting tickets: #{@ticket_waiting}"
|
||||||
|
br
|
||||||
|
|
||||||
|
.col-xs-6.col-sm-2.placeholder
|
||||||
|
= link_to admins_export_url do
|
||||||
|
= image_tag "icone_export.png", alt: "image export", class: "img-responsive", size: "128x128"
|
||||||
|
h4
|
||||||
|
abbr[title="This option only export the clients, contacts, and bills, without comments etc. If you want to export the complete database, use the native feature of your ORDBMS."] Export
|
||||||
|
span.text-muted = "Clients: #{@clients_count}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Contacts: #{@contacts_count}"
|
||||||
|
br
|
||||||
|
span.text-muted = "Bills: #{@bills_count}"
|
||||||
|
br
|
||||||
|
|
||||||
|
/ here, add a wonderful statistic graph with js
|
3
app/views/home/export.json.jbuilder
Normal file
3
app/views/home/export.json.jbuilder
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
json.clients(@clients)
|
||||||
|
json.contacts(@contacts)
|
||||||
|
json.bills(@bills)
|
53
app/views/home/help.html.slim
Normal file
53
app/views/home/help.html.slim
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
h1.page-header Help
|
||||||
|
|
||||||
|
- if user_signed_in?
|
||||||
|
.panel.panel-primary
|
||||||
|
.panel-heading
|
||||||
|
.panel-title Client
|
||||||
|
.panel-body
|
||||||
|
p
|
||||||
|
| Wecome to the Morning Peak CRM.
|
||||||
|
| This is an open source web application, designed for you, client of a provider, and your creditor.
|
||||||
|
| The application is compatible on Desktop, Tablet, and Mobiles.
|
||||||
|
h3 On Desktop
|
||||||
|
p
|
||||||
|
| With a large screen, you will be able to use the left sidebar panel. It allowes you to access to your
|
||||||
|
| dashboard (click on 'User'). You have also a link to the bills you due to your provider, and a links to the tickets,
|
||||||
|
| used to make easier the contact and conversation with him.
|
||||||
|
h3 On Tablet and Mobile
|
||||||
|
p
|
||||||
|
| With Tablets and Mobiles, you cannot see the sidebar panel. Instead, you will have an access to your dashboard,
|
||||||
|
| by using the square button on the top right.
|
||||||
|
br
|
||||||
|
| On the dashboard, you will see how many Bills you have (due, passed, ...) and the same for your tickets.
|
||||||
|
|
||||||
|
- if admin_signed_in?
|
||||||
|
.panel.panel-danger
|
||||||
|
.panel-heading
|
||||||
|
.panel-title Admin
|
||||||
|
.panel-body
|
||||||
|
p
|
||||||
|
| You can access to the list of your clients, the contacts for each clients,
|
||||||
|
| the bills your clients due to you and also the tickets to communicate with them.
|
||||||
|
|
||||||
|
- if not user_signed_in? and not admin_signed_in?
|
||||||
|
.panel.panel-primary
|
||||||
|
.panel-heading
|
||||||
|
.panel-title Connection
|
||||||
|
.panel-body
|
||||||
|
p
|
||||||
|
| To use this application, you have to sign in as a Client or Administrator with your créditentials.
|
||||||
|
br
|
||||||
|
= link_to "Sign in as User", new_user_session_path
|
||||||
|
br
|
||||||
|
| If you are an administrator, use this link instead
|
||||||
|
br
|
||||||
|
= link_to "Sign in as Admin", new_admin_session_path
|
||||||
|
br
|
||||||
|
|
||||||
|
p
|
||||||
|
| You can also contact the administrator, via the
|
||||||
|
= link_to "developpers on the website", "https://gitlab.com/poulet_a/MorningPeak/issues"
|
||||||
|
br
|
||||||
|
| Please, don't forget to be explicit and to write carefully.
|
||||||
|
| It the request is too much broad, then it will not be handled.
|
|
@ -3,22 +3,18 @@
|
||||||
.space-client.col-xs-4
|
.space-client.col-xs-4
|
||||||
.panel.panel-success
|
.panel.panel-success
|
||||||
.panel-heading
|
.panel-heading
|
||||||
h3
|
h3 = t "home.user_dashboard.title"
|
||||||
| Client
|
|
||||||
.glyphicon.glyphicon-log-in
|
|
||||||
.panel-body
|
.panel-body
|
||||||
- if user_signed_in?
|
- if user_signed_in?
|
||||||
= link_to "Log out", destroy_user_session_path, method: :delete
|
= link_to t(".links.logout"), destroy_user_session_path, method: :delete
|
||||||
- else
|
- else
|
||||||
= link_to "Log in", new_user_session_path
|
= link_to t(".links.login"), new_user_session_path
|
||||||
.space-admin.col-xs-4
|
.space-admin.col-xs-4
|
||||||
.panel.panel-danger
|
.panel.panel-danger
|
||||||
.panel-heading
|
.panel-heading
|
||||||
h3
|
h3 = t("home.admin_dashboard.title")
|
||||||
| Administration
|
|
||||||
.glyphicon.glyphicon-log-in
|
|
||||||
.panel-body
|
.panel-body
|
||||||
- if admin_signed_in?
|
- if admin_signed_in?
|
||||||
= link_to "Log out", destroy_admin_session_path, method: :delete
|
= link_to t(".links.logout"), destroy_admin_session_path, method: :delete
|
||||||
- else
|
- else
|
||||||
= link_to "Log in", new_admin_session_path
|
= link_to t(".links.login"), new_admin_session_path
|
||||||
|
|
28
app/views/home/user_dashboard.html.slim
Normal file
28
app/views/home/user_dashboard.html.slim
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
h1.page-header = t ".title"
|
||||||
|
|
||||||
|
.row.placeholders
|
||||||
|
.col-xs-6.col-sm-3.placeholder
|
||||||
|
= link_to client_bills_url do
|
||||||
|
= image_tag "icone_bill.png", alt: "image bills", class: "img-responsive", size: "128x128"
|
||||||
|
h4 = t "home.dashboard.bills"
|
||||||
|
span.text-muted = "#{t 'home.dashboard.retard_bills'}: #{@retard_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "#{t 'home.dashboard.advanced_bills'}: #{@advanced_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "#{t 'home.dashboard.next_bills'}: #{@retard_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "#{t 'home.dashboard.old_bills'}: #{@old_bills}"
|
||||||
|
br
|
||||||
|
span.text-muted = "#{t 'home.dashboard.total_bills'}: #{@bills_count}"
|
||||||
|
br
|
||||||
|
|
||||||
|
.col-xs-6.col-sm-3.placeholder
|
||||||
|
= link_to client_tickets_url do
|
||||||
|
= image_tag "icone_ticket.png", alt: "image tickets", class: "img-responsive", size: "128x128"
|
||||||
|
h4 = t "home.dashboard.tickets"
|
||||||
|
span.text-muted = "#{t 'home.dashboard.closed_tickets'}: #{@ticket_closed}"
|
||||||
|
br
|
||||||
|
span.text-muted = "#{t 'home.dashboard.opened_tickets'}: #{@ticket_opened}"
|
||||||
|
br
|
||||||
|
span.text-muted = "#{t 'home.dashboard.waiting_tickets'}: #{@ticket_waiting}"
|
||||||
|
br
|
11
app/views/layouts/_infos.html.slim
Normal file
11
app/views/layouts/_infos.html.slim
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#infos
|
||||||
|
- if alert
|
||||||
|
#alert.alert.bg-danger
|
||||||
|
span.glyphicon.glyphicon-exclamation-sign aria-hidden="true"
|
||||||
|
span.sr-only Error:
|
||||||
|
= " #{alert}"
|
||||||
|
- if notice
|
||||||
|
#notice.alert.bg-warning
|
||||||
|
span.glyphicon.glyphicon-info-sign aria-hidden="true"
|
||||||
|
span.sr-only Error:
|
||||||
|
= " #{notice}"
|
22
app/views/layouts/_navbar.html.slim
Normal file
22
app/views/layouts/_navbar.html.slim
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
nav.navbar.navbar-inverse.navbar-fixed-top
|
||||||
|
.container-fluid
|
||||||
|
#navbar-title.navbar-header
|
||||||
|
button.navbar-toggle.collapsed[type="button" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"]
|
||||||
|
span.sr-only Toggle navigation
|
||||||
|
span.icon-bar
|
||||||
|
span.icon-bar
|
||||||
|
span.icon-bar
|
||||||
|
.navbar-brand
|
||||||
|
= link_to application_title, root_url
|
||||||
|
|
||||||
|
#navbar.navbar-collapse.collapse
|
||||||
|
ul.nav.navbar-nav.navbar-right
|
||||||
|
- if user_signed_in?
|
||||||
|
li = link_to t(".user_dashboard"), users_dashboard_url
|
||||||
|
- if admin_signed_in?
|
||||||
|
li = link_to t(".admin_dashboard"), admins_dashboard_url
|
||||||
|
- if user_signed_in?
|
||||||
|
li = link_to "#{current_user.name} #{t '.logout'}", destroy_user_session_path, method: :delete
|
||||||
|
- if admin_signed_in?
|
||||||
|
li = link_to "#{current_admin.email} #{t '.logout'}", destroy_admin_session_path, method: :delete
|
||||||
|
li = link_to "Help", help_url
|
30
app/views/layouts/_sidebar.html.slim
Normal file
30
app/views/layouts/_sidebar.html.slim
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
.col-sm-3.col-md-2.sidebar
|
||||||
|
/ User side
|
||||||
|
- if user_signed_in?
|
||||||
|
ul.nav.nav-sidebar
|
||||||
|
li.active
|
||||||
|
= link_to users_dashboard_url do
|
||||||
|
= t ".user.user"
|
||||||
|
span.sr-only (current)
|
||||||
|
li = link_to client_bills_url do
|
||||||
|
= t ".user.bills_due"
|
||||||
|
.badge = user_bills_count_due
|
||||||
|
li = link_to client_tickets_url do
|
||||||
|
= t ".user.tickets"
|
||||||
|
.badge = user_tickets_unview_count
|
||||||
|
|
||||||
|
/ Admin side
|
||||||
|
- if admin_signed_in?
|
||||||
|
- if user_signed_in?
|
||||||
|
br
|
||||||
|
ul.nav.nav-sidebar
|
||||||
|
li.active
|
||||||
|
= link_to admins_dashboard_url do
|
||||||
|
= t(".admin.admin")
|
||||||
|
span.sr-only (current)
|
||||||
|
li = link_to t(".admin.clients"), clients_url
|
||||||
|
li = link_to t(".admin.contacts"), contacts_url
|
||||||
|
li = link_to t( ".admin.bills"), bills_url
|
||||||
|
li = link_to tickets_url do
|
||||||
|
= t ".admin.tickets"
|
||||||
|
.badge = admin_tickets_unview_count
|
|
@ -2,32 +2,21 @@ doctype html
|
||||||
html
|
html
|
||||||
head
|
head
|
||||||
title = application_title
|
title = application_title
|
||||||
|
meta charset="utf-8"
|
||||||
|
meta name="viewport" content="width=device-width, initial-scale=1"
|
||||||
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
|
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
|
||||||
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
|
|
||||||
body
|
body
|
||||||
nav.navbar.navbar-default
|
= render partial: "/layouts/navbar"
|
||||||
.container-fluid
|
|
||||||
.navbar-header
|
|
||||||
.navbar-brand = link_to application_title, root_url
|
|
||||||
|
|
||||||
.navbar-collapse.collapse
|
|
||||||
- if user_signed_in?
|
|
||||||
.nav.navbar-nav.navbar-left
|
|
||||||
| #{current_user.name}
|
|
||||||
= link_to " bills ", client_bills_url, class: 'btn btn-lg btn-success narbar-btn'
|
|
||||||
= link_to " tickets ", client_tickets_url, class: 'btn btn-lg btn-warning narbar-btn glyphicon glyphicon-envelope'
|
|
||||||
= link_to "", destroy_user_session_path, method: :delete, class: 'btn btn-lg btn-default narbar-btn glyphicon glyphicon-log-out'
|
|
||||||
- if admin_signed_in?
|
|
||||||
.nav.navbar-nav.navbar-right
|
|
||||||
| #{current_admin.email}
|
|
||||||
= link_to " clients ", clients_url, class: 'btn btn-lg btn-primary narbar-btn glyphicon glyphicon-user'
|
|
||||||
= link_to " contact ", contacts_url, class: 'btn btn-lg btn-info narbar-btn glyphicon glyphicon-duplicate'
|
|
||||||
= link_to " bills ", bills_url, class: 'btn btn-lg btn-success narbar-btn glyphicon glyphicon-eur'
|
|
||||||
= link_to " tickets", tickets_url, class: 'btn btn-lg btn-warning narbar-btn glyphicon-envelope'
|
|
||||||
= link_to "", destroy_admin_session_path, method: :delete, class: 'btn btn-lg btn-default narbar-btn glyphicon glyphicon-log-out'
|
|
||||||
|
|
||||||
.yield
|
|
||||||
.container-fluid
|
.container-fluid
|
||||||
|
.row
|
||||||
|
= render partial: "/layouts/sidebar"
|
||||||
|
|
||||||
|
#main.main.col-sm-9.col-sm-offset-3.col-md-10.col-md-offset-2.main
|
||||||
|
|
||||||
|
= render partial: "/layouts/infos"
|
||||||
|
#yield.yield
|
||||||
= yield
|
= yield
|
||||||
|
|
|
@ -6,22 +6,25 @@
|
||||||
- @ticket.errors.full_messages.each do |message|
|
- @ticket.errors.full_messages.each do |message|
|
||||||
li = message
|
li = message
|
||||||
|
|
||||||
|
.form-horizontal
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :ticket_id
|
= f.label :creator_type, class: "sr-only"
|
||||||
= f.text_field :ticket_id
|
= f.text_field :creator_type, class: "form-control", placeholder: "Creator type"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :creator_type
|
= f.label :creator_id, class: "sr-only"
|
||||||
= f.text_field :creator_type
|
= f.text_field :creator_id, class: "form-control", placeholder: "Creator ID"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :creator_id
|
= f.label :title, class: "sr-only"
|
||||||
= f.text_field :creator_id
|
= f.text_field :title, class: "form-control", placeholder: "Title"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :title
|
= f.label :description, class: "sr-only"
|
||||||
= f.text_field :title
|
= f.text_area :description, class: "form-control", placeholder: "Description"
|
||||||
|
.form-group
|
||||||
.field
|
.field
|
||||||
= f.label :description
|
= f.label :state, class: "sr-only"
|
||||||
= f.text_area :description
|
= f.select :state, ["open", "close"], {}, class: "form-control"
|
||||||
.field
|
.actions = f.submit class: "btn btn-success"
|
||||||
= f.label :state
|
|
||||||
= f.text_field :state
|
|
||||||
.actions = f.submit
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
h1 Editing ticket
|
h1.page-header Editing ticket
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
h1 Listing tickets
|
h1 Listing tickets
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
table.table.table-condensed.
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
td ID
|
td ID
|
||||||
|
@ -14,29 +14,27 @@ table.table.table-condensed.table-striped
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
th
|
||||||
th
|
|
||||||
|
|
||||||
tbody
|
tbody
|
||||||
- @tickets.each do |ticket|
|
- @tickets.each do |ticket|
|
||||||
tr
|
tr class="#{ticket.admin_view_at.nil? ? 'bg-warning' : ''}"
|
||||||
td = link_to("##{ticket.id}", ticket)
|
td = link_to("##{ticket.id}", ticket)
|
||||||
td = link_to_creator(ticket)
|
td = link_to_creator(ticket)
|
||||||
td = link_to ticket.title, ticket
|
td = link_to ticket.title, ticket
|
||||||
td = ticket.description
|
td = ticket.short_description
|
||||||
- if ticket.state == Ticket::CLOSE
|
- if ticket.state == Ticket::CLOSE
|
||||||
td.bg-danger = ticket.state
|
td.bg-danger = ticket.state
|
||||||
- else
|
- else
|
||||||
td.bg-success = ticket.state
|
td.bg-success = ticket.state
|
||||||
td = ticket.updated_at.to_s(:long)
|
td = ticket.updated_at.to_s(:long)
|
||||||
td = ticket.last_response ? distance_of_time_in_words_to_now(ticket.last_response.updated_at) : "never"
|
td = ticket.comments.empty? ? "never" : distance_of_time_in_words_to_now(ticket.comments.first.updated_at)
|
||||||
td = link_to '', ticket, class: 'glyphicon glyphicon-eye-open'
|
td = link_to 'Show', ticket
|
||||||
td = link_to '', edit_ticket_path(ticket), class: 'glyphicon glyphicon-pencil'
|
td = link_to 'Edit', edit_ticket_path(ticket)
|
||||||
td = link_to '', ticket, data: {:confirm => 'Are you sure to REMOVE this ticket?'}, :method => :delete, class: 'glyphicon glyphicon-remove-sign'
|
td = link_to 'Destroy', ticket, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||||
td = link_to '', ticket_respond_path(ticket), class: 'glyphicon glyphicon-comment' if ticket.open?
|
|
||||||
- if ticket.open?
|
- if ticket.open?
|
||||||
td = link_to '', ticket_close_path(ticket), data: {:confirm => 'Are you sure to CLOSE this ticket?'}, method: :patch, class: 'glyphicon glyphicon-ok-circle'
|
td = link_to 'Close', ticket_close_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||||
- elsif ticket.close?
|
- elsif ticket.close?
|
||||||
td = link_to '', ticket_open_path(ticket), data: {:confirm => 'Are you sure to REOPEN this ticket?'}, method: :patch, class: 'glyphicon glyphicon-remove-circle'
|
td = link_to 'Reopen', ticket_open_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
json.array!(@tickets) do |ticket|
|
json.array!(@tickets) do |ticket|
|
||||||
json.extract! ticket, :id, :client_id, :bill_id, :title, :description, :state
|
json.extract! ticket, :id, :title, :description, :state
|
||||||
json.url ticket_url(ticket, format: :json)
|
json.url ticket_url(ticket, format: :json)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
h1 New ticket
|
h1.page-header New ticket
|
||||||
|
|
||||||
== render 'form'
|
== render 'form'
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
p#notice = notice
|
h1.page-header = "Show ticket: #{@ticket.title}"
|
||||||
|
|
||||||
p
|
p
|
||||||
strong
|
strong
|
||||||
| Id: #
|
| Id: #
|
||||||
= @ticket.id
|
= @ticket.id
|
||||||
p
|
|
||||||
strong Réponse à:
|
|
||||||
= link_to @ticket.head.title, @ticket.head
|
|
||||||
|
|
||||||
p
|
p
|
||||||
strong State:
|
strong State:
|
||||||
- if @ticket.head.state == Ticket::CLOSE
|
- if @ticket.state == Ticket::CLOSE
|
||||||
td.bg-danger = @ticket.head.state
|
td.bg-danger = @ticket.state
|
||||||
- else
|
- else
|
||||||
td.bg-success = @ticket.head.state
|
td.bg-success = @ticket.state
|
||||||
|
|
||||||
p
|
p
|
||||||
strong Creator:
|
strong Creator:
|
||||||
|
@ -23,40 +20,19 @@ p
|
||||||
= @ticket.title
|
= @ticket.title
|
||||||
p
|
p
|
||||||
strong Description:
|
strong Description:
|
||||||
= @ticket.description
|
.container-fluid.ticket-description
|
||||||
|
= descriptionize @ticket.description
|
||||||
p
|
p
|
||||||
strong State:
|
strong State:
|
||||||
= @ticket.state
|
= @ticket.state
|
||||||
|
|
||||||
table.table.table-condensed.table-striped
|
|
||||||
thead
|
|
||||||
tr
|
|
||||||
th Creator
|
|
||||||
th Title
|
|
||||||
th Description
|
|
||||||
th Last update
|
|
||||||
th
|
|
||||||
th
|
|
||||||
th
|
|
||||||
|
|
||||||
tbody
|
|
||||||
- @ticket.tickets.each do |ticket|
|
|
||||||
tr
|
|
||||||
td = link_to_creator(ticket)
|
|
||||||
td = link_to ticket.title, ticket
|
|
||||||
td = ticket.description
|
|
||||||
- if ticket.state == Ticket::CLOSE
|
|
||||||
td.bg-danger = ticket.state
|
|
||||||
- else
|
|
||||||
td.bg-success = ticket.state
|
|
||||||
td = ticket.updated_at.to_s(:long)
|
|
||||||
td = link_to 'Show', ticket
|
|
||||||
td = link_to 'Edit', edit_ticket_path(ticket)
|
|
||||||
td = link_to 'Destroy', ticket, data: {:confirm => 'Are you sure?'}, :method => :delete
|
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
|
- if @ticket.open?
|
||||||
= link_to 'Respond', ticket_respond_path(@ticket), class: 'btn btn-sm btn-default'
|
= link_to 'Close', ticket_close_path(@ticket), method: :patch, class: 'btn btn-sm btn-success'
|
||||||
|
- else
|
||||||
|
= link_to 'Reopen', ticket_open_path(@ticket), method: :patch, class: 'btn btn-sm btn-danger'
|
||||||
= link_to 'Edit', edit_ticket_path(@ticket), class: 'btn btn-sm btn-default'
|
= link_to 'Edit', edit_ticket_path(@ticket), class: 'btn btn-sm btn-default'
|
||||||
= link_to 'Back', tickets_path, class: 'btn btn-sm btn-default'
|
= link_to 'Back', tickets_path, class: 'btn btn-sm btn-default'
|
||||||
|
|
||||||
|
== render partial: "/comments/thread", locals: {commentable: @ticket, type: 'admin'}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
json.extract! @ticket, :id, :client_id, :bill_id, :title, :description, :state, :created_at, :updated_at
|
json.extract! @ticket, :id, :title, :description, :state, :created_at, :updated_at
|
||||||
|
|
|
@ -6,7 +6,7 @@ require 'rails/all'
|
||||||
# you've limited to :test, :development, or :production.
|
# you've limited to :test, :development, or :production.
|
||||||
Bundler.require(*Rails.groups)
|
Bundler.require(*Rails.groups)
|
||||||
|
|
||||||
module WebImmo
|
module MorningPeak
|
||||||
class Application < Rails::Application
|
class Application < Rails::Application
|
||||||
# Settings in config/environments/* take precedence over those specified here.
|
# Settings in config/environments/* take precedence over those specified here.
|
||||||
# Application configuration should go into files in config/initializers
|
# Application configuration should go into files in config/initializers
|
||||||
|
|
|
@ -1,46 +1,14 @@
|
||||||
development:
|
default: &default
|
||||||
adapter: postgresql
|
adapter: postgresql
|
||||||
host: localhost
|
|
||||||
username: root
|
username: root
|
||||||
database: appli_dev
|
|
||||||
password: toor
|
password: toor
|
||||||
|
|
||||||
|
development:
|
||||||
|
<<: *default
|
||||||
|
host: localhost
|
||||||
|
database: webimmo_dev
|
||||||
|
|
||||||
test:
|
test:
|
||||||
adapter: postgresql
|
<<: *default
|
||||||
host: localhost
|
host: localhost
|
||||||
username: root
|
database: webimmo_test
|
||||||
database: appli_test
|
|
||||||
password: toor
|
|
||||||
|
|
||||||
production:
|
|
||||||
adapter: postgresql
|
|
||||||
host: localhost
|
|
||||||
username: root
|
|
||||||
database: appli_prod
|
|
||||||
password: toor
|
|
||||||
|
|
||||||
# # SQLite version 3.x
|
|
||||||
# # gem install sqlite3
|
|
||||||
# #
|
|
||||||
# # Ensure the SQLite 3 gem is defined in your Gemfile
|
|
||||||
# # gem 'sqlite3'
|
|
||||||
# #
|
|
||||||
# default: &default
|
|
||||||
# adapter: sqlite3
|
|
||||||
# pool: 5
|
|
||||||
# timeout: 5000
|
|
||||||
|
|
||||||
# development:
|
|
||||||
# <<: *default
|
|
||||||
# database: db/development.sqlite3
|
|
||||||
|
|
||||||
# # Warning: The database defined as "test" will be erased and
|
|
||||||
# # re-generated from your development database when you run "rake".
|
|
||||||
# # Do not set this db to the same as development or production.
|
|
||||||
# test:
|
|
||||||
# <<: *default
|
|
||||||
# database: db/test.sqlite3
|
|
||||||
|
|
||||||
# production:
|
|
||||||
# <<: *default
|
|
||||||
# database: db/production.sqlite3
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
Rails.application.config.session_store :cookie_store, key: '_WebImmo_session'
|
Rails.application.config.session_store :cookie_store, key: '_MorningPeak_session'
|
||||||
|
|
77
config/locales/bills.yml
Normal file
77
config/locales/bills.yml
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
fr:
|
||||||
|
bills:
|
||||||
|
form:
|
||||||
|
client_id: ID du Client
|
||||||
|
title: Titre
|
||||||
|
description: Détail
|
||||||
|
amount: Montant
|
||||||
|
is_paid: Payé ?
|
||||||
|
e_date: "Date d'emission"
|
||||||
|
d_date: "Date d'échéance"
|
||||||
|
edit:
|
||||||
|
page:
|
||||||
|
title: Éditer une facture
|
||||||
|
new:
|
||||||
|
page:
|
||||||
|
title: Nouvelle facture
|
||||||
|
index:
|
||||||
|
page:
|
||||||
|
title: Liste des factures
|
||||||
|
id: ID
|
||||||
|
client: Client
|
||||||
|
Title: Titre
|
||||||
|
description: Detail
|
||||||
|
amount: Montant
|
||||||
|
is_paid: Payé ?
|
||||||
|
e_date: "Date d'emission"
|
||||||
|
d_date: "Date d'échéance"
|
||||||
|
show:
|
||||||
|
page:
|
||||||
|
title: Facture
|
||||||
|
id: ID
|
||||||
|
client: Client
|
||||||
|
Title: Titre
|
||||||
|
description: Detail
|
||||||
|
amount: Montant
|
||||||
|
is_paid: Payé ?
|
||||||
|
e_date: "Date d'emission"
|
||||||
|
d_date: "Date d'échéance"
|
||||||
|
|
||||||
|
en:
|
||||||
|
bills:
|
||||||
|
form:
|
||||||
|
client_id: Client ID
|
||||||
|
title: Title
|
||||||
|
description: Description
|
||||||
|
amount: Amount
|
||||||
|
is_paid: Paid ?
|
||||||
|
e_date: Emission date
|
||||||
|
d_date: Due date
|
||||||
|
edit:
|
||||||
|
page:
|
||||||
|
title: Edit a Bill
|
||||||
|
new:
|
||||||
|
page:
|
||||||
|
title: New Bill
|
||||||
|
index:
|
||||||
|
page:
|
||||||
|
title: Listing Bills
|
||||||
|
id: ID
|
||||||
|
client: Client
|
||||||
|
title: Title
|
||||||
|
description: Description
|
||||||
|
amount: Amount
|
||||||
|
is_paid: Paid ?
|
||||||
|
e_date: Emission date
|
||||||
|
d_date: Due date
|
||||||
|
show:
|
||||||
|
page:
|
||||||
|
title: Bill
|
||||||
|
id: ID
|
||||||
|
client: Client
|
||||||
|
title: Title
|
||||||
|
description: Description
|
||||||
|
amount: Amount
|
||||||
|
is_paid: Paid ?
|
||||||
|
e_date: Emission date
|
||||||
|
d_date: Due date
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user