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
|
||||
.env
|
||||
.env*
|
||||
|
||||
lolcommit.yml
|
||||
*.swp
|
||||
|
|
30
Gemfile
30
Gemfile
|
@ -1,6 +1,5 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
|
||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||
gem 'rails', '4.2.1'
|
||||
# Use sqlite3 as the database for Active Record
|
||||
|
@ -16,6 +15,7 @@ gem 'coffee-rails', '~> 4.1.0'
|
|||
|
||||
# Use jquery as the JavaScript library
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails'
|
||||
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
||||
gem 'turbolinks'
|
||||
# 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.
|
||||
group :doc do
|
||||
gem 'sdoc', '~> 0.4.0'
|
||||
gem 'erd'
|
||||
gem 'rails-erd'
|
||||
end
|
||||
|
||||
# Use ActiveModel has_secure_password
|
||||
|
@ -50,19 +50,25 @@ end
|
|||
|
||||
# Web Server
|
||||
gem 'puma'
|
||||
# Database
|
||||
gem 'pg'
|
||||
# Upload
|
||||
gem 'carrierwave'
|
||||
# Environnement .env
|
||||
gem 'dotenv-rails'
|
||||
# Database
|
||||
gem 'pg'
|
||||
|
||||
# Users
|
||||
gem 'devise'
|
||||
# Pagination
|
||||
gem 'will_paginate'
|
||||
# Bootstrap
|
||||
gem 'bootstrap-sass'
|
||||
# Bootstrap for pagination
|
||||
gem 'bootstrap-will_paginate'
|
||||
# Upload
|
||||
gem 'carrierwave'
|
||||
# Commentable items
|
||||
gem 'acts_as_commentable'
|
||||
|
||||
# HTML Templating
|
||||
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)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
arel (6.0.0)
|
||||
autoprefixer-rails (5.2.0.1)
|
||||
execjs
|
||||
json
|
||||
acts_as_commentable (4.0.2)
|
||||
arel (6.0.3)
|
||||
bcrypt (3.1.10)
|
||||
binding_of_caller (0.7.2)
|
||||
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)
|
||||
will_paginate
|
||||
builder (3.2.2)
|
||||
|
@ -74,24 +69,24 @@ GEM
|
|||
responders
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
dotenv (2.0.1)
|
||||
dotenv-rails (2.0.1)
|
||||
dotenv (= 2.0.1)
|
||||
erd (0.3.2)
|
||||
nokogiri
|
||||
rails-erd (>= 0.4.5)
|
||||
dotenv (2.0.2)
|
||||
dotenv-rails (2.0.2)
|
||||
dotenv (= 2.0.2)
|
||||
railties (~> 4.0)
|
||||
erubis (2.7.0)
|
||||
execjs (2.5.2)
|
||||
globalid (0.3.5)
|
||||
globalid (0.3.6)
|
||||
activesupport (>= 4.1.0)
|
||||
i18n (0.7.0)
|
||||
jbuilder (2.3.0)
|
||||
jbuilder (2.3.1)
|
||||
activesupport (>= 3.0.0, < 5)
|
||||
multi_json (~> 1.2)
|
||||
jquery-rails (4.0.4)
|
||||
rails-dom-testing (~> 1.0)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (5.0.5)
|
||||
railties (>= 3.2.16)
|
||||
json (1.8.3)
|
||||
loofah (2.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
|
@ -100,8 +95,8 @@ GEM
|
|||
method_source (0.8.2)
|
||||
mime-types (2.6.1)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.7.0)
|
||||
multi_json (1.11.1)
|
||||
minitest (5.8.0)
|
||||
multi_json (1.11.2)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
orm_adapter (0.5.0)
|
||||
|
@ -110,9 +105,8 @@ GEM
|
|||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
puma (2.11.3)
|
||||
rack (>= 1.1, < 2.0)
|
||||
rack (1.6.2)
|
||||
puma (2.12.3)
|
||||
rack (1.6.4)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.1)
|
||||
|
@ -132,13 +126,15 @@ GEM
|
|||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-erd (1.4.0)
|
||||
rails-erd (1.4.2)
|
||||
activerecord (>= 3.2)
|
||||
activesupport (>= 3.2)
|
||||
choice (~> 0.2.0)
|
||||
ruby-graphviz (~> 1.2)
|
||||
rails-html-sanitizer (1.0.2)
|
||||
loofah (~> 2.0)
|
||||
rails-jquery-autocomplete (1.0.3)
|
||||
rails (>= 3.2)
|
||||
railties (4.2.1)
|
||||
actionpack (= 4.2.1)
|
||||
activesupport (= 4.2.1)
|
||||
|
@ -149,7 +145,7 @@ GEM
|
|||
responders (2.1.0)
|
||||
railties (>= 4.2.0, < 5)
|
||||
ruby-graphviz (1.2.2)
|
||||
sass (3.4.14)
|
||||
sass (3.4.16)
|
||||
sass-rails (5.0.3)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.1)
|
||||
|
@ -172,7 +168,7 @@ GEM
|
|||
spring (1.3.6)
|
||||
sprockets (3.2.0)
|
||||
rack (~> 1.0)
|
||||
sprockets-rails (2.3.1)
|
||||
sprockets-rails (2.3.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
|
@ -182,6 +178,11 @@ GEM
|
|||
tilt (1.4.1)
|
||||
turbolinks (2.5.3)
|
||||
coffee-rails
|
||||
twitter-bootstrap-rails (3.2.0)
|
||||
actionpack (~> 4.1)
|
||||
execjs (~> 2.2)
|
||||
rails (~> 4.1)
|
||||
railties (~> 4.1)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.7.1)
|
||||
|
@ -189,7 +190,7 @@ GEM
|
|||
json (>= 1.8.0)
|
||||
warden (1.2.3)
|
||||
rack (>= 1.0)
|
||||
web-console (2.1.3)
|
||||
web-console (2.2.1)
|
||||
activemodel (>= 4.0)
|
||||
binding_of_caller (>= 0.7.2)
|
||||
railties (>= 4.0)
|
||||
|
@ -200,28 +201,31 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
bootstrap-sass
|
||||
acts_as_commentable
|
||||
bootstrap-will_paginate
|
||||
byebug
|
||||
carrierwave
|
||||
coffee-rails (~> 4.1.0)
|
||||
devise
|
||||
dotenv-rails
|
||||
erd
|
||||
jbuilder (~> 2.0)
|
||||
jquery-rails
|
||||
jquery-ui-rails
|
||||
pg
|
||||
pry
|
||||
puma
|
||||
rails (= 4.2.1)
|
||||
rails-erd
|
||||
rails-jquery-autocomplete
|
||||
sass-rails (~> 5.0)
|
||||
sdoc (~> 0.4.0)
|
||||
slim-rails
|
||||
spring
|
||||
turbolinks
|
||||
twitter-bootstrap-rails
|
||||
uglifier (>= 1.3.0)
|
||||
web-console (~> 2.0)
|
||||
will_paginate
|
||||
|
||||
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>.
|
||||
|
140
README.md
140
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``.
|
||||
- Then, be sure you have postgresql installed, and started.
|
||||
<img att="Dashboard Mobile view" src="http://imgur.com/bnYHYJ6l.png" width="200" />
|
||||
<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
|
||||
cd Appli/
|
||||
cd MorningPeak/
|
||||
gem install bundler
|
||||
bundle install
|
||||
cp config/database.yml.example config/database.yml
|
||||
edit config/database.yml # Configure your database connection first
|
||||
rake db:create # create the db
|
||||
rake db:migrate # migrate the db
|
||||
edit config/database.yml # Configure your database connection first
|
||||
rake db:create
|
||||
rake db:migrate
|
||||
rake db:seed # will generate default data. Not on production ;)
|
||||
```
|
||||
|
||||
you can try with an first example of dataset :
|
||||
|
||||
```
|
||||
rake db:seed
|
||||
## 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
|
||||
- A ``.env`` file looks like:
|
||||
```text
|
||||
COMPANY: "Yolo production"
|
||||
LOCALE: fr
|
||||
...
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
You can run the server by :
|
||||
- The application's specifics cvars are :
|
||||
- COMPANY
|
||||
|
||||
## 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
|
||||
- __/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
|
||||
Checkout for [rails minidoc](RailsMinidoc.md) for a resume of rails.
|
||||
|
||||
# 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_ujs
|
||||
//= require turbolinks
|
||||
//= require jquery-ui
|
||||
//= require twitter/bootstrap
|
||||
//= require jquery-ui/autocomplete
|
||||
//= require autocomplete-rails
|
||||
//= 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.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# 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;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
&:visited {
|
||||
color: #666;
|
||||
}
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
/* a { */
|
||||
/* color: #000; */
|
||||
/* &:visited { */
|
||||
/* color: #666; */
|
||||
/* } */
|
||||
/* &:hover { */
|
||||
/* color: #fff; */
|
||||
/* background-color: #000; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
div {
|
||||
&.field, &.actions {
|
||||
|
@ -35,9 +35,9 @@ div {
|
|||
}
|
||||
}
|
||||
|
||||
#notice {
|
||||
color: green;
|
||||
}
|
||||
/* #notice { */
|
||||
/* color: green; */
|
||||
/* } */
|
||||
|
||||
.field_with_errors {
|
||||
padding: 2px;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
// Place all the styles related to the tickets controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
.ticket-description {
|
||||
border: 1px dashed black;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ticket-creator {
|
||||
color: #d9534f;
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
class ApplicationController < ActionController::Base
|
||||
# Prevent CSRF attacks by raising an exception.
|
||||
# For APIs, you may want to use :null_session instead.
|
||||
|
||||
protect_from_forgery with: :exception
|
||||
|
||||
# before_action :authenticate_user!
|
||||
# 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
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
class BillsController < ApplicationController
|
||||
include CommentableForm
|
||||
|
||||
autocomplete :client, :name
|
||||
|
||||
before_action :set_bill, only: [:show, :edit, :update, :destroy]
|
||||
before_action :authenticate_admin!
|
||||
|
||||
|
@ -11,6 +15,7 @@ class BillsController < ApplicationController
|
|||
# GET /bills/1
|
||||
# GET /bills/1.json
|
||||
def show
|
||||
prepare_comment_for @bill
|
||||
end
|
||||
|
||||
# GET /bills/new
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
class ClientTicketsController < ApplicationController
|
||||
include CommentableForm
|
||||
|
||||
before_action :set_ticket, only: [:show, :edit, :update, :destroy]
|
||||
before_action :set_ticket_custom_route, only: [:close, :open, :respond]
|
||||
before_action :looks_ticket, only: [:show, :edit, :update, :close, :open]
|
||||
before_action :authenticate_user!
|
||||
|
||||
# GET /tickets
|
||||
# GET /tickets.json
|
||||
def index
|
||||
@tickets = current_user.tickets.heads.order('state DESC')
|
||||
@tickets = current_user.tickets.order('state DESC')
|
||||
end
|
||||
|
||||
def close
|
||||
respond_to do |format|
|
||||
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) }
|
||||
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 }
|
||||
end
|
||||
end
|
||||
|
@ -24,33 +28,20 @@ class ClientTicketsController < ApplicationController
|
|||
def open
|
||||
respond_to do |format|
|
||||
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) }
|
||||
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 }
|
||||
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.json
|
||||
def show
|
||||
prepare_comment_for @ticket
|
||||
end
|
||||
|
||||
# GET /tickets/new
|
||||
|
@ -66,13 +57,11 @@ class ClientTicketsController < ApplicationController
|
|||
# POST /tickets.json
|
||||
def create
|
||||
@ticket = Ticket.new(ticket_params)
|
||||
@ticket.ticket_id = session[:user_ticket_respond_parent_id]
|
||||
@ticket.creator = current_user
|
||||
|
||||
respond_to do |format|
|
||||
if @ticket.save
|
||||
@ticket.close_head if @ticket.close?
|
||||
format.html { redirect_to @ticket, notice: 'Ticket was successfully created.' }
|
||||
format.html { redirect_to client_ticket_url(@ticket), notice: 'Ticket was successfully created.' }
|
||||
format.json { render :show, status: :created, location: client_tickets_url(@ticket) }
|
||||
else
|
||||
format.html { render :new }
|
||||
|
@ -86,7 +75,6 @@ class ClientTicketsController < ApplicationController
|
|||
def update
|
||||
respond_to do |format|
|
||||
if @ticket.update(ticket_params)
|
||||
@ticket.close_head if @ticket.close?
|
||||
format.html { redirect_to @ticket, notice: 'Ticket was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: client_tickets_url(@ticket) }
|
||||
else
|
||||
|
@ -107,21 +95,25 @@ class ClientTicketsController < ApplicationController
|
|||
# end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_ticket
|
||||
@ticket = Ticket.find(params[:id])
|
||||
@ticket = nil if not @ticket.creator == current_user and not @ticket.head.creator == current_user
|
||||
@ticket
|
||||
end
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_ticket
|
||||
@ticket = Ticket.find_by(id: params[:id], creator: current_user)
|
||||
@ticket
|
||||
end
|
||||
|
||||
def set_ticket_custom_route
|
||||
params[:id] = params[:client_ticket_id]
|
||||
set_ticket
|
||||
end
|
||||
# TODO: remove
|
||||
def set_ticket_custom_route
|
||||
params[:id] = params[:client_ticket_id]
|
||||
set_ticket
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def ticket_params
|
||||
params.require(:ticket).permit(:title, :description, :state)
|
||||
end
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def ticket_params
|
||||
params.require(:ticket).permit(:title, :description, :state)
|
||||
end
|
||||
|
||||
def looks_ticket
|
||||
@ticket.set_view_by('User')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
class ClientsController < ApplicationController
|
||||
before_action :set_client, only: [:show, :edit, :update, :destroy]
|
||||
include CommentableForm
|
||||
|
||||
autocomplete :contact, :name
|
||||
|
||||
before_action :authenticate_admin!
|
||||
before_action :set_client, only: [:show, :edit, :update, :destroy]
|
||||
|
||||
# GET /clients
|
||||
# GET /clients.json
|
||||
|
@ -11,6 +15,7 @@ class ClientsController < ApplicationController
|
|||
# GET /clients/1
|
||||
# GET /clients/1.json
|
||||
def show
|
||||
prepare_comment_for @client
|
||||
end
|
||||
|
||||
# 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
|
||||
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!
|
||||
|
||||
# PATCH /view/1
|
||||
def view
|
||||
@contact.update(view_at: Time.now)
|
||||
render json: @contact, status: :ok
|
||||
end
|
||||
|
||||
# GET /contacts
|
||||
# GET /contacts.json
|
||||
def index
|
||||
|
@ -11,6 +21,7 @@ class ContactsController < ApplicationController
|
|||
# GET /contacts/1
|
||||
# GET /contacts/1.json
|
||||
def show
|
||||
prepare_comment_for @contact
|
||||
end
|
||||
|
||||
# GET /contacts/new
|
||||
|
@ -63,13 +74,13 @@ class ContactsController < ApplicationController
|
|||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_contact
|
||||
@contact = Contact.find(params[:id])
|
||||
end
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_contact
|
||||
@contact = Contact.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def contact_params
|
||||
params.require(:contact).permit(:client_id, :name, :phone, :email, :last_contact, :note, :region, :department, :postal_code, :address)
|
||||
end
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def contact_params
|
||||
params.require(:contact).permit(:client_id, :name, :phone, :email, :note, :region, :department, :postal_code, :address)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,37 @@
|
|||
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
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
class TicketsController < ApplicationController
|
||||
include CommentableForm
|
||||
|
||||
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!
|
||||
|
||||
# GET /tickets
|
||||
# GET /tickets.json
|
||||
def index
|
||||
@tickets = Ticket.heads.order('state DESC')
|
||||
@tickets = Ticket.order('state DESC')
|
||||
end
|
||||
|
||||
def close
|
||||
respond_to do |format|
|
||||
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 }
|
||||
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 }
|
||||
end
|
||||
end
|
||||
|
@ -24,32 +28,20 @@ class TicketsController < ApplicationController
|
|||
def open
|
||||
respond_to do |format|
|
||||
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 }
|
||||
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 }
|
||||
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.json
|
||||
def show
|
||||
prepare_comment_for @ticket
|
||||
end
|
||||
|
||||
# GET /tickets/new
|
||||
|
@ -84,7 +76,6 @@ class TicketsController < ApplicationController
|
|||
def update
|
||||
respond_to do |format|
|
||||
if @ticket.update(ticket_params)
|
||||
@ticket.close_head if @ticket.close?
|
||||
format.html { redirect_to @ticket, notice: 'Ticket was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: @ticket }
|
||||
else
|
||||
|
@ -105,18 +96,22 @@ class TicketsController < ApplicationController
|
|||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_ticket
|
||||
@ticket = Ticket.find(params[:id])
|
||||
end
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_ticket
|
||||
@ticket = Ticket.find(params[:id])
|
||||
end
|
||||
|
||||
def set_ticket_custom_route
|
||||
@ticket = Ticket.find(params[:ticket_id])
|
||||
end
|
||||
def set_ticket_custom_route
|
||||
@ticket = Ticket.find(params[:ticket_id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def ticket_params
|
||||
params.require(:ticket).permit(:ticket_id, :title, :description, :state)
|
||||
end
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def ticket_params
|
||||
params.require(:ticket).permit(:title, :description, :state)
|
||||
end
|
||||
|
||||
def looks_ticket
|
||||
@ticket.set_view_by('Admin')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module ApplicationHelper
|
||||
|
||||
def application_title
|
||||
['Appli', ENV['COMPANY'], 'client gestion'].join(' ')
|
||||
['MorningPeak', ENV['COMPANY'], 'CRM'].compact.join(' ')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
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
|
||||
|
|
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
|
||||
|
||||
def user_tickets_unview_count
|
||||
return nil if not user_signed_in?
|
||||
return current_user.tickets_unview.count
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -8,4 +8,39 @@ module TicketsHelper
|
|||
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
|
||||
|
|
|
@ -9,5 +9,10 @@ class Admin < ActiveRecord::Base
|
|||
#:validatable
|
||||
|
||||
has_many :tickets, as: :creator
|
||||
has_many :comments, as: :creator
|
||||
|
||||
def tickets_unview
|
||||
Ticket.all.where(admin_view_at: nil)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
class Bill < ActiveRecord::Base
|
||||
acts_as_commentable
|
||||
belongs_to :client
|
||||
|
||||
validates :due_date, presence: 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
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
class Client < ActiveRecord::Base
|
||||
acts_as_commentable
|
||||
|
||||
has_many :contacts
|
||||
has_many :bills
|
||||
has_many :tickets, through: :user
|
||||
|
@ -8,7 +10,7 @@ class Client < ActiveRecord::Base
|
|||
delegate :name, to: :contact, allow_nil: true, prefix: true
|
||||
|
||||
def last_contact
|
||||
contacts.pluck('last_contact').max || "never"
|
||||
contacts.where('view_at IS NOT NULL').pluck(:view_at).max || "never"
|
||||
end
|
||||
|
||||
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
|
||||
acts_as_commentable
|
||||
belongs_to :client
|
||||
|
||||
delegate :name, to: :client, prefix: true
|
||||
|
|
|
@ -1,58 +1,83 @@
|
|||
# coding: utf-8
|
||||
class Ticket < ActiveRecord::Base
|
||||
acts_as_commentable
|
||||
|
||||
# WARNING ! IT REVERSE LAST / FIRST
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
OPEN = 'open'
|
||||
CLOSE = 'close'
|
||||
|
||||
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
|
||||
delegate :name, to: :creator, prefix: true
|
||||
|
||||
belongs_to :ticket
|
||||
has_many :tickets
|
||||
|
||||
scope :heads, -> { where(ticket_id: nil) }
|
||||
scope :open, -> { heads.where(state: OPEN) }
|
||||
scope :close, -> { heads.where(state: CLOSE) }
|
||||
scope :open, -> { where(state: OPEN) }
|
||||
scope :close, -> { where(state: CLOSE) }
|
||||
|
||||
before_save :check_creator
|
||||
def check_creator
|
||||
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)
|
||||
end
|
||||
|
||||
def last_response
|
||||
head.tickets.last
|
||||
after_create :set_view_init
|
||||
def set_view_init
|
||||
set_view_by(creator_type)
|
||||
end
|
||||
|
||||
def head
|
||||
return self if not ticket
|
||||
return ticket.head
|
||||
def set_view_by(viewer_type)
|
||||
if creator_type == 'Admin'
|
||||
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
|
||||
|
||||
def close
|
||||
if close?
|
||||
errors.add "Already close"
|
||||
errors[:base] << "Already close"
|
||||
return false
|
||||
end
|
||||
head.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)
|
||||
update(state: Ticket::CLOSE)
|
||||
end
|
||||
|
||||
def open
|
||||
if open?
|
||||
errors.add "Already open"
|
||||
errors[:base] << "Already open"
|
||||
return false
|
||||
end
|
||||
head.update(state: Ticket::OPEN) && update(state: Ticket::OPEN)
|
||||
update(state: Ticket::OPEN)
|
||||
end
|
||||
|
||||
def open?
|
||||
|
@ -63,4 +88,8 @@ class Ticket < ActiveRecord::Base
|
|||
state == CLOSE
|
||||
end
|
||||
|
||||
def short_description
|
||||
description.to_s.first(100)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -10,12 +10,19 @@ class User < ActiveRecord::Base
|
|||
has_one :client
|
||||
delegate :name, to: :client
|
||||
has_many :tickets, as: :creator
|
||||
has_many :comments, as: :creator
|
||||
has_many :contacts, through: :client
|
||||
has_many :bills, through: :client
|
||||
|
||||
after_create :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
|
||||
|
|
|
@ -6,25 +6,38 @@
|
|||
- @bill.errors.full_messages.each do |message|
|
||||
li = message
|
||||
|
||||
.field
|
||||
= f.label :client_id
|
||||
= f.text_field :client_id
|
||||
.field
|
||||
= f.label :title
|
||||
= f.text_field :title
|
||||
.field
|
||||
= f.label :description
|
||||
= f.text_area :description
|
||||
.field
|
||||
= f.label :amount
|
||||
= f.number_field :amount
|
||||
.field
|
||||
= f.label :paid
|
||||
= f.check_box :paid
|
||||
.field
|
||||
= f.label :emission_date
|
||||
= f.date_select :emission_date
|
||||
.field
|
||||
= f.label :due_date
|
||||
= f.date_select :due_date
|
||||
.actions = f.submit
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.field
|
||||
= f.autocomplete_field :client, autocomplete_client_name_bills_path, :id_element => '#bill_client_id', class: "form-control", placeholder: "Client Name"
|
||||
= f.label :client_id, class: "sr-only"
|
||||
= f.text_field :client_id, class: "form-control sr-only", placeholder: "#{t('.client_id')}"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :title, class: "sr-only"
|
||||
= f.text_field :title, class: "form-control", placeholder: "#{t('.title')}"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :description, class: "sr-only"
|
||||
= f.text_area :description, class: "form-control", placeholder: "#{t('.description')}"
|
||||
.form-inline
|
||||
.form-group
|
||||
.field
|
||||
= f.label :amount, class: "sr-only"
|
||||
= f.number_field :amount, class: "form-control", placeholder: "#{t('.amount')}"
|
||||
.form-group
|
||||
.field
|
||||
= f.label t('.is_paid')
|
||||
br
|
||||
= f.check_box :paid, class: "form-control", placeholder: "#{t('.is_paid')}"
|
||||
.form-inline
|
||||
.form-group
|
||||
.field
|
||||
= f.label :emission_date, class: "sr-only"
|
||||
= f.text_field :emission_date, class: "form-control form-date", placeholder: "#{t('.e_date')}"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :due_date, class: "sr-only"
|
||||
= f.text_field :due_date, class: "form-control form-date", placeholder: "#{t('.d_date')}"
|
||||
|
||||
.actions = f.submit class: "btn btn-success"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
h1 Editing bill
|
||||
h1.page-header = t(".page.title")
|
||||
|
||||
== render 'form'
|
||||
|
||||
= link_to 'Show', @bill, class: 'btn btn-sm btn-default'
|
||||
= link_to 'Back', bills_path, class: 'btn btn-sm btn-default'
|
||||
= link_to t("show"), @bill, 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
|
||||
tr
|
||||
td ID
|
||||
th Client
|
||||
th Title
|
||||
th Description
|
||||
th Amount
|
||||
th Paid
|
||||
th Emission date
|
||||
th Due date
|
||||
th = t(".id")
|
||||
th = t(".client")
|
||||
th = t(".title")
|
||||
th = t(".description")
|
||||
th = t(".amount")
|
||||
th = t(".paid")
|
||||
th = t(".e_date")
|
||||
th = t(".d_date")
|
||||
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)
|
||||
- else
|
||||
td.bg-info = distance_of_time_in_words_to_now(bill.due_date).to_s + " - " + bill.due_date.to_s(:long)
|
||||
td = link_to 'Show', bill
|
||||
td = link_to 'Edit', edit_bill_path(bill)
|
||||
td = link_to 'Destroy', bill, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||
td = link_to t('show'), bill
|
||||
td = link_to t('edit'), edit_bill_path(bill)
|
||||
td = link_to t('destroy'), bill, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||
|
||||
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'
|
||||
|
||||
= 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
|
||||
strong Client:
|
||||
strong = t ".client"
|
||||
= link_to @bill.client.name, @bill.client
|
||||
p
|
||||
strong Title:
|
||||
strong = t ".title"
|
||||
= @bill.title
|
||||
p
|
||||
strong Description:
|
||||
strong = t ".description"
|
||||
= @bill.description
|
||||
p
|
||||
strong Amount:
|
||||
strong = t ".amount"
|
||||
= @bill.amount
|
||||
p
|
||||
strong Paid:
|
||||
strong = t ".is_paid"
|
||||
= @bill.paid
|
||||
p
|
||||
strong Emission date:
|
||||
strong = t ".e_date"
|
||||
= @bill.emission_date
|
||||
p
|
||||
strong Due date:
|
||||
strong = t ".d_date"
|
||||
- if @bill.due_date < Date.today
|
||||
- if @bill.paid
|
||||
.bg-success = distance_of_time_in_words_to_now(@bill.due_date) + " - " + @bill.due_date.to_s(:long)
|
||||
|
@ -28,5 +28,10 @@ p
|
|||
- else
|
||||
.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 'Back', bills_path, class: 'btn btn-sm btn-default'
|
||||
= link_to t('edit'), edit_bill_path(@bill), 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
|
||||
tr
|
||||
td ID
|
||||
th Title
|
||||
th Description
|
||||
th Amount
|
||||
th Paid
|
||||
th Emission date
|
||||
th Due date
|
||||
th ID
|
||||
th = t '.title'
|
||||
th = t '.description'
|
||||
th = t '.amount'
|
||||
th = t '.paid'
|
||||
th = t '.emission_date'
|
||||
th = t '.due_date'
|
||||
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)
|
||||
- else
|
||||
td.bg-info = distance_of_time_in_words_to_now(bill.due_date).to_s + " - " + bill.due_date.to_s(:long)
|
||||
td = link_to 'Show', bill
|
||||
td = link_to t('show'), client_bill_url(bill)
|
||||
|
||||
br
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
p#notice = notice
|
||||
|
||||
h1.page-header = t '.page.title'
|
||||
p
|
||||
strong Title:
|
||||
strong = t ".title"
|
||||
= @bill.title
|
||||
p
|
||||
strong Description:
|
||||
strong = t ".description"
|
||||
= @bill.description
|
||||
p
|
||||
strong Amount:
|
||||
strong = t ".amount"
|
||||
= @bill.amount
|
||||
p
|
||||
strong Paid:
|
||||
strong = t ".paid"
|
||||
= @bill.paid
|
||||
p
|
||||
strong Emission date:
|
||||
strong = t ".emission_date"
|
||||
= @bill.emission_date
|
||||
p
|
||||
strong Due date:
|
||||
strong = t ".due_date"
|
||||
- if @bill.due_date < Date.today
|
||||
- if @bill.paid
|
||||
.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?
|
||||
#error_explanation
|
||||
h2 = "#{pluralize(@ticket.errors.count, "error")} prohibited this ticket from being saved:"
|
||||
|
@ -6,13 +6,18 @@
|
|||
- @ticket.errors.full_messages.each do |message|
|
||||
li = message
|
||||
|
||||
.field
|
||||
= f.label :title
|
||||
= f.text_field :title
|
||||
.field
|
||||
= f.label :description
|
||||
= f.text_area :description
|
||||
.field
|
||||
= f.label :state
|
||||
= f.text_field :state
|
||||
.actions = f.submit
|
||||
.form-group
|
||||
.field
|
||||
= f.label :title, class: "sr-only"
|
||||
= f.text_field :title, class: "form-control", placeholder: "#{t('.title')}"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :description, class: "sr-only"
|
||||
= 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
|
||||
= f.label t('.state')
|
||||
= f.select :state, ["open", "close"], {}, class: "form-control"
|
||||
.actions = f.submit class: "btn btn-default"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
h1 Editing ticket
|
||||
h1.page-header = t(".page.title")
|
||||
|
||||
== render 'form'
|
||||
|
||||
= link_to '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('show'), client_ticket_path(@ticket), 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
|
||||
tr
|
||||
td ID
|
||||
th Creator
|
||||
th Title
|
||||
th Description
|
||||
th State
|
||||
th Last update
|
||||
th Last response
|
||||
th
|
||||
th ID
|
||||
th = t ".title"
|
||||
th = t ".description"
|
||||
th = t ".state"
|
||||
th = t ".last_update"
|
||||
th = t ".last_response"
|
||||
th
|
||||
th
|
||||
th
|
||||
|
||||
tbody
|
||||
- @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_creator(ticket)
|
||||
td = link_to ticket.title, client_ticket_path(ticket)
|
||||
td = ticket.description
|
||||
td = ticket.short_description
|
||||
- if ticket.state == Ticket::CLOSE
|
||||
td.bg-danger = ticket.state
|
||||
td.bg-danger = t ".states.close"
|
||||
- else
|
||||
td.bg-success = ticket.state
|
||||
td.bg-success = t ".states.open"
|
||||
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 = link_to 'Show', ticket
|
||||
td = link_to 'Edit', edit_client_ticket_path(ticket)
|
||||
/ td = link_to 'Destroy', ticket, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||
td = link_to 'Respond', ticket_respond_path(ticket) if ticket.open?
|
||||
td = ticket.comments.empty? ? "never" : distance_of_time_in_words_to_now(ticket.comments.first.updated_at)
|
||||
td = link_to (t "show"), ticket
|
||||
td = link_to (t "edit"), edit_client_ticket_path(ticket)
|
||||
- if ticket.open?
|
||||
td = link_to 'Close', client_ticket_close_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||
td = link_to (t "close"), client_ticket_close_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||
- elsif ticket.close?
|
||||
td = link_to 'Reopen', client_ticket_open_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||
td = link_to (t "reopen"), client_ticket_open_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||
|
||||
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'
|
||||
|
||||
= 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
|
||||
strong
|
||||
| Id: #
|
||||
= @ticket.id
|
||||
p
|
||||
strong Réponse à:
|
||||
= link_to @ticket.head.title, client_ticket_path(@ticket.head)
|
||||
strong = t ".title"
|
||||
|:
|
||||
= link_to @ticket.title, client_ticket_path(@ticket)
|
||||
|
||||
p
|
||||
strong State:
|
||||
- if @ticket.head.state == Ticket::CLOSE
|
||||
td.bg-danger = @ticket.head.state
|
||||
strong = t ".state"
|
||||
|:
|
||||
- if @ticket.state == Ticket::CLOSE
|
||||
td.bg-danger = t ".states.close"
|
||||
- else
|
||||
td.bg-success = @ticket.head.state
|
||||
td.bg-success = t ".states.open"
|
||||
|
||||
p
|
||||
strong Creator:
|
||||
= link_to_creator @ticket
|
||||
strong = t ".creator"
|
||||
|:
|
||||
span.ticket-creator = link_to_creator @ticket
|
||||
p
|
||||
strong Title:
|
||||
strong = t ".title"
|
||||
|:
|
||||
= @ticket.title
|
||||
p
|
||||
strong Description:
|
||||
= @ticket.description
|
||||
p
|
||||
strong State:
|
||||
= @ticket.state
|
||||
strong = t ".description"
|
||||
|:
|
||||
.container-fluid.ticket-description
|
||||
= descriptionize @ticket.description
|
||||
|
||||
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, 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
|
||||
== render partial: "/comments/thread", locals: {commentable: @ticket, type: 'user'}
|
||||
|
||||
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|
|
||||
li = message
|
||||
|
||||
.field
|
||||
= f.label :name
|
||||
= f.text_field :name
|
||||
.field
|
||||
= f.label :url
|
||||
= f.text_field :url
|
||||
.field
|
||||
= f.label :activity
|
||||
= f.text_field :activity
|
||||
.field
|
||||
= f.label :contact_reasons
|
||||
= f.text_area :contact_reasons
|
||||
.field
|
||||
= f.label :user_id
|
||||
= f.text_field :user_id
|
||||
.field
|
||||
= f.label :contact_id
|
||||
= f.text_field :contact_id
|
||||
.actions = f.submit
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.field
|
||||
= f.label :name, class: "sr-only"
|
||||
= f.text_field :name, class: "form-control", placeholder: "Name"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :url, class: "sr-only"
|
||||
= f.text_field :url, class: "form-control", placeholder: "URL"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :activity, class: "sr-only"
|
||||
= f.text_field :activity, class: "form-control", placeholder: "Activity"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :contact_reasons, class: "sr-only"
|
||||
= f.text_area :contact_reasons, class: "form-control", placeholder: "Contact reasons"
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.field
|
||||
= f.label :user_id, class: "sr-only"
|
||||
= f.text_field :user_id, class: "form-control", placeholder: "User ID"
|
||||
.form-group
|
||||
.field
|
||||
= f.autocomplete_field :contact, autocomplete_contact_name_clients_path, :id_element => '#client_contact_id', class: "form-control", placeholder: "Main Contact Name"
|
||||
= f.label :contact_id, class: "sr-only"
|
||||
= 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'
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
h1 Listing clients
|
||||
h1.page-header Listing clients
|
||||
|
||||
table.table.table-condensed.table-striped
|
||||
table.table.table-condensed
|
||||
thead
|
||||
tr
|
||||
td ID
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
h1 New client
|
||||
h1.page-header New client
|
||||
|
||||
== render 'form'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
p#notice = notice
|
||||
h1.page-header Show Client
|
||||
|
||||
p
|
||||
strong Name:
|
||||
|
@ -29,50 +29,58 @@ p
|
|||
= link_to 'Edit', edit_client_path(@client), class: 'btn btn-sm btn-default'
|
||||
= link_to 'Back', clients_path, class: 'btn btn-sm btn-default'
|
||||
|
||||
table.table.table-condensed
|
||||
caption Contacts list
|
||||
tr
|
||||
th Name
|
||||
th Phone
|
||||
th Email
|
||||
- @client.contacts.each do |contact|
|
||||
tr
|
||||
td = link_to contact.name, contact
|
||||
td = contact.phone
|
||||
td = contact.email
|
||||
#contacts.container-fluid
|
||||
h2 Contacts list
|
||||
table.table.table-condensed
|
||||
tr
|
||||
th Name
|
||||
th Phone
|
||||
th Email
|
||||
- @client.contacts.each do |contact|
|
||||
tr
|
||||
td = link_to contact.name, contact
|
||||
td = contact.phone
|
||||
td = contact.email
|
||||
|
||||
table.table.table-condensed
|
||||
caption = "Retarded Due Bills list #{@client.bills_retard.pluck(:amount).sum} €"
|
||||
tr
|
||||
th Title
|
||||
th Amount
|
||||
th Due date
|
||||
- @client.bills_retard.each do |bill|
|
||||
tr
|
||||
td.bg-danger = link_to bill.title, bill
|
||||
td.bg-danger = bill.amount
|
||||
td.bg-danger = bill.due_date
|
||||
#bills.container-fluid
|
||||
h2 Bills list
|
||||
table.table.table-condensed
|
||||
caption = "Retarded Due Bills list #{@client.bills_retard.pluck(:amount).sum} €"
|
||||
tr
|
||||
th Title
|
||||
th Amount
|
||||
th Due date
|
||||
- @client.bills_retard.each do |bill|
|
||||
tr
|
||||
td.bg-danger = link_to bill.title, bill
|
||||
td.bg-danger = bill.amount
|
||||
td.bg-danger = bill.due_date
|
||||
|
||||
table.table.table-condensed
|
||||
caption = "Next Bills list #{@client.bills_next.pluck(:amount).sum} €"
|
||||
tr
|
||||
th Title
|
||||
th Amount
|
||||
th Due date
|
||||
- @client.bills_next.each do |bill|
|
||||
tr
|
||||
td.bg-info = link_to bill.title, bill
|
||||
td.bg-info = bill.amount
|
||||
td.bg-info = bill.due_date
|
||||
table.table.table-condensed
|
||||
caption = "Next Bills list #{@client.bills_next.pluck(:amount).sum} €"
|
||||
tr
|
||||
th Title
|
||||
th Amount
|
||||
th Due date
|
||||
- @client.bills_next.each do |bill|
|
||||
tr
|
||||
td.bg-info = link_to bill.title, bill
|
||||
td.bg-info = bill.amount
|
||||
td.bg-info = bill.due_date
|
||||
|
||||
table.table.table-condensed
|
||||
caption = "Old Bills list #{@client.bills_old.pluck(:amount).sum} €"
|
||||
tr
|
||||
th Title
|
||||
th Amount
|
||||
th Due date
|
||||
- @client.bills_old.each do |bill|
|
||||
tr
|
||||
td.bg-success = link_to bill.title, bill
|
||||
td.bg-success = bill.amount
|
||||
td.bg-success = bill.due_date
|
||||
table.table.table-condensed
|
||||
caption = "Old Bills list #{@client.bills_old.pluck(:amount).sum} €"
|
||||
tr
|
||||
th Title
|
||||
th Amount
|
||||
th Due date
|
||||
- @client.bills_old.each do |bill|
|
||||
tr
|
||||
td.bg-success = link_to bill.title, bill
|
||||
td.bg-success = bill.amount
|
||||
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|
|
||||
li = message
|
||||
|
||||
.field
|
||||
= f.label :client_id
|
||||
= f.text_field :client_id
|
||||
.field
|
||||
= f.label :name
|
||||
= f.text_field :name
|
||||
.field
|
||||
= f.label :phone
|
||||
= f.text_field :phone
|
||||
.field
|
||||
= f.label :email
|
||||
= f.text_field :email
|
||||
.field
|
||||
= f.label :last_contact
|
||||
= f.date_select :last_contact
|
||||
.field
|
||||
= f.label :note
|
||||
= f.text_area :note
|
||||
.field
|
||||
= f.label :region
|
||||
= f.text_field :region
|
||||
.field
|
||||
= f.label :department
|
||||
= f.text_field :department
|
||||
.field
|
||||
= f.label :postal_code
|
||||
= f.text_field :postal_code
|
||||
.field
|
||||
= f.label :address
|
||||
= f.text_area :address
|
||||
.actions = f.submit
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.field
|
||||
= f.autocomplete_field :client, autocomplete_client_name_contacts_path, :id_element => '#contact_client_id', class: "form-control", placeholder: "Client Name"
|
||||
= 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
|
||||
= f.label :name, class: "sr-only"
|
||||
= f.text_field :name, class: "form-control", placeholder: "Name"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :phone, class: "sr-only"
|
||||
= f.text_field :phone, class: "form-control", placeholder: "Phone"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :email, class: "sr-only"
|
||||
= f.text_field :email, class: "form-control", placeholder: "Email"
|
||||
.form-inline
|
||||
.form-group
|
||||
.field
|
||||
= f.label :region, class: "sr-only"
|
||||
= f.text_field :region, class: "form-control", placeholder: "Region"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :department, class: "sr-only"
|
||||
= f.text_field :department, class: "form-control", placeholder: "Department"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :postal_code, class: "sr-only"
|
||||
= f.text_field :postal_code, class: "form-control", placeholder: "Postal Code"
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.field
|
||||
= f.label :address, class: "sr-only"
|
||||
= f.text_area :address, class: "form-control", placeholder: "Address"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :note, class: "sr-only"
|
||||
= f.text_area :note, class: "form-control", placeholder: "Note"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :view_at, class: "sr-only"
|
||||
= f.text_field :view_at, class: "form-control form-date", placeholder: "View at"
|
||||
.actions = f.submit class: "btn btn-success"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
h1 Editing contact
|
||||
h1.page-header Editing contact
|
||||
|
||||
== render 'form'
|
||||
|
||||
= link_to 'Show', @contact
|
||||
'|
|
||||
= 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
|
||||
tr
|
||||
td ID
|
||||
|
@ -8,12 +8,10 @@ table.table.table-condensed.table-striped
|
|||
th Name
|
||||
th Phone
|
||||
th Email
|
||||
th Last contact
|
||||
th Note
|
||||
th Region
|
||||
th Department
|
||||
th Postal code
|
||||
th Address
|
||||
th Last view
|
||||
th
|
||||
th
|
||||
th
|
||||
th
|
||||
|
@ -26,12 +24,12 @@ table.table.table-condensed.table-striped
|
|||
td = link_to contact.name, contact
|
||||
td = contact.phone
|
||||
td = contact.email
|
||||
td = contact.last_contact
|
||||
td = contact.note
|
||||
td = contact.region
|
||||
td = contact.department
|
||||
td = contact.postal_code
|
||||
td = contact.address
|
||||
td.contact-value-view_at
|
||||
= contact.view_at
|
||||
td.contact-link-view_at
|
||||
= 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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
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)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
h1 New contact
|
||||
h1.page-header New contact
|
||||
|
||||
== render 'form'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
p#notice = notice
|
||||
h1.page-header Show contact
|
||||
|
||||
p
|
||||
strong Client:
|
||||
|
@ -13,8 +13,8 @@ p
|
|||
strong Email:
|
||||
= @contact.email
|
||||
p
|
||||
strong Last contact:
|
||||
= @contact.last_contact
|
||||
strong View at:
|
||||
= @contact.view_at
|
||||
p
|
||||
strong Note:
|
||||
= @contact.note
|
||||
|
@ -33,3 +33,8 @@ p
|
|||
|
||||
= link_to 'Edit', edit_contact_path(@contact), 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,18 +3,18 @@
|
|||
.space-client.col-xs-4
|
||||
.panel.panel-success
|
||||
.panel-heading
|
||||
h3 Se connecter comme client
|
||||
h3 = t "home.user_dashboard.title"
|
||||
.panel-body
|
||||
- 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
|
||||
= link_to "Log in", new_user_session_path
|
||||
= link_to t(".links.login"), new_user_session_path
|
||||
.space-admin.col-xs-4
|
||||
.panel.panel-danger
|
||||
.panel-heading
|
||||
h3 Se connecter comme admin
|
||||
h3 = t("home.admin_dashboard.title")
|
||||
.panel-body
|
||||
- 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
|
||||
= 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
|
||||
head
|
||||
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
|
||||
= javascript_include_tag 'application', 'data-turbolinks-track' => true
|
||||
= csrf_meta_tags
|
||||
|
||||
body
|
||||
nav.navbar.navbar-default
|
||||
.container-fluid
|
||||
.navbar-header
|
||||
.navbar-brand = link_to application_title, root_url
|
||||
= render partial: "/layouts/navbar"
|
||||
|
||||
.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'
|
||||
= link_to "logout", destroy_user_session_path, method: :delete, class: 'btn btn-lg btn-default narbar-btn'
|
||||
- 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'
|
||||
= link_to "contacts", contacts_url, class: 'btn btn-lg btn-info narbar-btn'
|
||||
= link_to "bills", bills_url, class: 'btn btn-lg btn-success narbar-btn'
|
||||
= link_to "tickets", tickets_url, class: 'btn btn-lg btn-warning narbar-btn'
|
||||
= link_to "logout", destroy_admin_session_path, method: :delete, class: 'btn btn-lg btn-default narbar-btn'
|
||||
.container-fluid
|
||||
.row
|
||||
= render partial: "/layouts/sidebar"
|
||||
|
||||
.yield
|
||||
.container-fluid
|
||||
= yield
|
||||
#main.main.col-sm-9.col-sm-offset-3.col-md-10.col-md-offset-2.main
|
||||
|
||||
= render partial: "/layouts/infos"
|
||||
#yield.yield
|
||||
= yield
|
||||
|
|
|
@ -6,22 +6,25 @@
|
|||
- @ticket.errors.full_messages.each do |message|
|
||||
li = message
|
||||
|
||||
.field
|
||||
= f.label :ticket_id
|
||||
= f.text_field :ticket_id
|
||||
.field
|
||||
= f.label :creator_type
|
||||
= f.text_field :creator_type
|
||||
.field
|
||||
= f.label :creator_id
|
||||
= f.text_field :creator_id
|
||||
.field
|
||||
= f.label :title
|
||||
= f.text_field :title
|
||||
.field
|
||||
= f.label :description
|
||||
= f.text_area :description
|
||||
.field
|
||||
= f.label :state
|
||||
= f.text_field :state
|
||||
.actions = f.submit
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.field
|
||||
= f.label :creator_type, class: "sr-only"
|
||||
= f.text_field :creator_type, class: "form-control", placeholder: "Creator type"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :creator_id, class: "sr-only"
|
||||
= f.text_field :creator_id, class: "form-control", placeholder: "Creator ID"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :title, class: "sr-only"
|
||||
= f.text_field :title, class: "form-control", placeholder: "Title"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :description, class: "sr-only"
|
||||
= f.text_area :description, class: "form-control", placeholder: "Description"
|
||||
.form-group
|
||||
.field
|
||||
= f.label :state, class: "sr-only"
|
||||
= f.select :state, ["open", "close"], {}, class: "form-control"
|
||||
.actions = f.submit class: "btn btn-success"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
h1 Editing ticket
|
||||
h1.page-header Editing ticket
|
||||
|
||||
== render 'form'
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
h1 Listing tickets
|
||||
|
||||
table.table.table-condensed.table-striped
|
||||
table.table.table-condensed.
|
||||
thead
|
||||
tr
|
||||
td ID
|
||||
|
@ -14,25 +14,23 @@ table.table.table-condensed.table-striped
|
|||
th
|
||||
th
|
||||
th
|
||||
th
|
||||
|
||||
tbody
|
||||
- @tickets.each do |ticket|
|
||||
tr
|
||||
tr class="#{ticket.admin_view_at.nil? ? 'bg-warning' : ''}"
|
||||
td = link_to("##{ticket.id}", ticket)
|
||||
td = link_to_creator(ticket)
|
||||
td = link_to ticket.title, ticket
|
||||
td = ticket.description
|
||||
td = ticket.short_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 = 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 'Show', ticket
|
||||
td = link_to 'Edit', edit_ticket_path(ticket)
|
||||
td = link_to 'Destroy', ticket, data: {:confirm => 'Are you sure?'}, :method => :delete
|
||||
td = link_to 'Respond', ticket_respond_path(ticket) if ticket.open?
|
||||
- if ticket.open?
|
||||
td = link_to 'Close', ticket_close_path(ticket), data: {:confirm => 'Are you sure?'}, :method => :patch
|
||||
- elsif ticket.close?
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
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)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
h1 New ticket
|
||||
h1.page-header New ticket
|
||||
|
||||
== render 'form'
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
p#notice = notice
|
||||
h1.page-header = "Show ticket: #{@ticket.title}"
|
||||
|
||||
p
|
||||
strong
|
||||
| Id: #
|
||||
= @ticket.id
|
||||
p
|
||||
strong Réponse à:
|
||||
= link_to @ticket.head.title, @ticket.head
|
||||
|
||||
p
|
||||
strong State:
|
||||
- if @ticket.head.state == Ticket::CLOSE
|
||||
td.bg-danger = @ticket.head.state
|
||||
- if @ticket.state == Ticket::CLOSE
|
||||
td.bg-danger = @ticket.state
|
||||
- else
|
||||
td.bg-success = @ticket.head.state
|
||||
td.bg-success = @ticket.state
|
||||
|
||||
p
|
||||
strong Creator:
|
||||
|
@ -23,40 +20,19 @@ p
|
|||
= @ticket.title
|
||||
p
|
||||
strong Description:
|
||||
= @ticket.description
|
||||
.container-fluid.ticket-description
|
||||
= descriptionize @ticket.description
|
||||
p
|
||||
strong 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
|
||||
|
||||
|
||||
= link_to 'Respond', ticket_respond_path(@ticket), class: 'btn btn-sm btn-default'
|
||||
- if @ticket.open?
|
||||
= 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 '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.
|
||||
Bundler.require(*Rails.groups)
|
||||
|
||||
module WebImmo
|
||||
module MorningPeak
|
||||
class Application < Rails::Application
|
||||
# Settings in config/environments/* take precedence over those specified here.
|
||||
# Application configuration should go into files in config/initializers
|
||||
|
|
|
@ -1,46 +1,14 @@
|
|||
development:
|
||||
default: &default
|
||||
adapter: postgresql
|
||||
host: localhost
|
||||
username: root
|
||||
database: appli_dev
|
||||
password: toor
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
host: localhost
|
||||
database: webimmo_dev
|
||||
|
||||
test:
|
||||
adapter: postgresql
|
||||
<<: *default
|
||||
host: localhost
|
||||
username: root
|
||||
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
|
||||
database: webimmo_test
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# 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