diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2ddd07d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.gradle
+*.ipr
+*.iml
+*.iws
+build/*
+modules/*/build/*
+out
+test-run
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9cac4b7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,21 @@
+# Dashboard Sample
+
+This is a showcase [CUBA](https://www.cuba-platform.com/) application which demonstrates how to customize system screens using CssLayout.
+
+The application has fully customized dashboard which uses `CssLayout` with [Vaadin responsive extention](https://vaadin.com/wiki/-/wiki/Main/Responsive+layouts+using+the+Responsive+extension).
+
+* The `ExtAppMainWindow` controller extends the `AppMainWindow` controller defined in the platform.
+* The `BootstrapListener` class extends the `CubaBootstrapListener` to add `viewport` metatag with `device-width` option.
+* The `ext-mainwindow.xml` screen extends the `mainwindow.xml` screen defined in the platform.
+* Additional CSS rules for the dashboard are defined in `dashboard.scss` file.
+
+To see the example in action, do the following:
+
+* Open the project in Studio
+* Click *Run > Start application server*
+* If you see the warning *Database does not exist ...*, click *Create DB in background*
+* Go to `http://localhost:8080/app` and log in as `admin` / `admin`
+
+You can see this demo in action at [www.cuba-platform.com](https://demo.cuba-platform.com/dashboard/).
+
+Based on CUBA Platform 6.1.5
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..4a75bcb
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,242 @@
+
+buildscript {
+ ext.cubaVersion = '6.1.5'
+ repositories {
+ maven {
+ url 'https://repo.cuba-platform.com/content/groups/work'
+ credentials {
+ username(rootProject.hasProperty('repoUser') ? rootProject['repoUser'] : 'cuba')
+ password(rootProject.hasProperty('repoPass') ? rootProject['repoPass'] : 'cuba123')
+ }
+ }
+ maven {
+ url 'https://repo.cuba-platform.com/content/groups/premium'
+ credentials {
+ username(rootProject.hasProperty('premiumRepoUser') ? rootProject['premiumRepoUser'] : System.getenv('CUBA_PREMIUM_USER'))
+ password(rootProject.hasProperty('premiumRepoPass') ? rootProject['premiumRepoPass'] : System.getenv('CUBA_PREMIUM_PASSWORD'))
+ }
+ }
+ }
+ dependencies {
+ classpath "com.haulmont.gradle:cuba-plugin:$cubaVersion"
+ }
+}
+
+def globalModule = project(':dashboard-global')
+def coreModule = project(':dashboard-core')
+def guiModule = project(':dashboard-gui')
+def webModule = project(':dashboard-web')
+
+def servletApi = 'org.apache.tomcat:tomcat-servlet-api:8.0.26'
+
+apply(plugin: 'idea')
+apply(plugin: 'cuba')
+
+cuba {
+ artifact {
+ group = 'com.company.dashboard'
+ version = '0.1'
+ isSnapshot = true
+ }
+ tomcat {
+ dir = "$project.rootDir/build/tomcat"
+ port = 8082
+ shutdownPort = 8007
+ ajpPort = 8011
+ }
+ ide {
+ copyright = '''Copyright (c) ${today.year} ${project.name}''' // Copyright Notice for IDEA project
+ classComment ='''/**
+ * @author ${USER}
+ */'''
+ vcs = 'Git'
+
+ }
+}
+
+def postgres = 'org.postgresql:postgresql:9.4-1201-jdbc41'
+
+configure([globalModule, coreModule, guiModule, webModule]) {
+ apply(plugin: 'java')
+ apply(plugin: 'maven')
+ apply(plugin: 'idea')
+ apply(plugin: 'cuba')
+
+ dependencies {
+ testCompile('junit:junit:4.12')
+ }
+
+ task sourceJar(type: Jar) {
+ from file('src')
+ classifier = 'sources'
+ }
+
+ artifacts {
+ archives sourceJar
+ }
+}
+
+configure(globalModule) {
+ dependencies {
+ compile("com.haulmont.cuba:cuba-global:$cubaVersion")
+ compile("com.haulmont.charts:charts-global:$cubaVersion")
+
+ }
+
+ task enhance(type: CubaEnhancing)
+
+}
+
+configure(coreModule) {
+
+ configurations {
+ jdbc
+ dbscripts
+ }
+
+ dependencies {
+ compile(globalModule)
+ provided(servletApi)
+ jdbc(postgres)
+ testRuntime(postgres)
+ compile("com.haulmont.cuba:cuba-core:$cubaVersion")
+ testCompile("com.haulmont.cuba:cuba-core-tests:$cubaVersion")
+ testCompile("com.haulmont.cuba:cuba-shared-lib:$cubaVersion")
+ dbscripts("com.haulmont.cuba:cuba-core:$cubaVersion:db@zip")
+
+ }
+
+ task cleanConf(description: 'Cleans up conf directory') << {
+ def dir = new File(cuba.tomcat.dir, '/conf/dashboard-core')
+ if (dir.isDirectory()) {
+ ant.delete(includeemptydirs: true) {
+ fileset(dir: dir, includes: '**/*', excludes: 'local.app.properties')
+ }
+ }
+ }
+
+ task deploy(dependsOn: [assemble, cleanConf], type: CubaDeployment) {
+ appName = 'dashboard-core'
+ appJars('cuba-core', 'cuba-global', 'charts-global',
+ 'dashboard-global', 'dashboard-core')
+ }
+
+ task createDb(dependsOn: assembleDbScripts, description: 'Creates local database', type: CubaDbCreation) {
+ dbms = 'postgres'
+ dbmsVersion = 'null'
+ host = 'localhost'
+ dbName = 'demo'
+ dbUser = 'cuba'
+ dbPassword = 'cuba'
+ }
+
+ task updateDb(dependsOn: assembleDbScripts, description: 'Updates local database', type: CubaDbUpdate) {
+ dbms = 'postgres'
+ dbmsVersion = 'null'
+ host = 'localhost'
+ dbName = 'demo'
+ dbUser = 'cuba'
+ dbPassword = 'cuba'
+ }
+}
+
+configure(guiModule) {
+ dependencies {
+ compile(globalModule)
+ compile("com.haulmont.cuba:cuba-gui:$cubaVersion")
+ testCompile("com.haulmont.cuba:cuba-client-tests:$cubaVersion")
+ testCompile('org.jmockit:jmockit:1.15')
+ compile("com.haulmont.charts:charts-gui:$cubaVersion")
+ testCompile('org.jmockit:jmockit:1.15')
+
+ }
+
+ task deployConf(type: Copy) {
+ from file('src')
+ include "com/company/dashboard/**"
+ into "$cuba.tomcat.dir/conf/dashboard"
+ }
+}
+
+configure(webModule) {
+ configurations {
+ webcontent
+ themes
+
+ }
+
+ dependencies {
+ provided(servletApi)
+ compile(guiModule)
+ compile("com.haulmont.cuba:cuba-web:$cubaVersion")
+ webcontent("com.haulmont.cuba:cuba-web:$cubaVersion:web@zip")
+ testCompile("com.haulmont.cuba:cuba-client-tests:$cubaVersion")
+ testCompile('org.jmockit:jmockit:1.15')
+ compile("com.haulmont.charts:charts-web:$cubaVersion")
+ webcontent("com.haulmont.charts:charts-web:$cubaVersion:web@zip")
+ webcontent("com.haulmont.charts:charts-web-toolkit:$cubaVersion:web@zip")
+ testCompile('org.jmockit:jmockit:1.15')
+ themes("com.haulmont.cuba:cuba-web-themes:$cubaVersion")
+ provided("com.haulmont.cuba:cuba-web-themes:$cubaVersion")
+
+ }
+
+ task webArchive(type: Zip) {
+ from file('web')
+ classifier = 'web'
+ }
+
+ artifacts {
+ archives webArchive
+ }
+
+ task deployConf(type: Copy) {
+ from file('src')
+ include "com/company/dashboard/**"
+ into "$cuba.tomcat.dir/conf/dashboard"
+ }
+
+ task clearMessagesCache(type: CubaClearMessagesCache) {
+ appName = 'dashboard'
+ }
+ deployConf.dependsOn clearMessagesCache
+
+ task cleanConf(description: 'Cleans up conf directory') << {
+ def dir = new File(cuba.tomcat.dir, '/conf/dashboard')
+ if (dir.isDirectory()) {
+ ant.delete(includeemptydirs: true) {
+ fileset(dir: dir, includes: '**/*', excludes: 'local.app.properties')
+ }
+ }
+ }
+
+ task deploy(dependsOn: [assemble, cleanConf], type: CubaDeployment) {
+ appName = 'dashboard'
+ appJars('cuba-gui', 'cuba-client', 'charts-gui', 'cuba-web', 'cuba-rest-api', 'charts-web', 'cuba-global', 'charts-global',
+ 'dashboard-global', 'dashboard-gui', 'dashboard-web')
+ }
+ task buildScssThemes(type: CubaWebScssThemeCreation)
+ task deployThemes(type: CubaDeployThemeTask, dependsOn: buildScssThemes)
+ assemble.dependsOn buildScssThemes
+}
+
+task undeploy(type: Delete, dependsOn: ':dashboard-web:cleanConf') {
+ delete("$cuba.tomcat.dir/shared")
+ delete("$cuba.tomcat.dir/webapps/dashboard-core")
+ delete("$cuba.tomcat.dir/webapps/dashboard")
+}
+
+task restart(dependsOn: ['stop', ':dashboard-core:deploy', ':dashboard-web:deploy'], description: 'Redeploys applications and restarts local Tomcat') << {
+ ant.waitfor(maxwait: 6, maxwaitunit: 'second', checkevery: 2, checkeveryunit: 'second') {
+ not {
+ socket(server: 'localhost', port: '8787')
+ }
+ }
+ start.execute()
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '2.6'
+}
+
+apply from: 'extra.gradle'
diff --git a/extra.gradle b/extra.gradle
new file mode 100644
index 0000000..e69de29
diff --git a/modules/core/db/init/postgres/10.create-db.sql b/modules/core/db/init/postgres/10.create-db.sql
new file mode 100644
index 0000000..e69de29
diff --git a/modules/core/db/init/postgres/20.create-db.sql b/modules/core/db/init/postgres/20.create-db.sql
new file mode 100644
index 0000000..e69de29
diff --git a/modules/core/db/init/postgres/30.create-db.sql b/modules/core/db/init/postgres/30.create-db.sql
new file mode 100644
index 0000000..7b2a836
--- /dev/null
+++ b/modules/core/db/init/postgres/30.create-db.sql
@@ -0,0 +1,4 @@
+insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
+values ('15672452-c835-dcd8-1bdb-35efcdefefda', now(), 0, 'demo', 'demo',
+'c956de0f1cb49c302777ded5dd8bf85848b21ca1',
+'Demo', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true);
\ No newline at end of file
diff --git a/modules/core/db/update/postgres/16/160419-0-createDemoUser.sql b/modules/core/db/update/postgres/16/160419-0-createDemoUser.sql
new file mode 100644
index 0000000..7b2a836
--- /dev/null
+++ b/modules/core/db/update/postgres/16/160419-0-createDemoUser.sql
@@ -0,0 +1,4 @@
+insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
+values ('15672452-c835-dcd8-1bdb-35efcdefefda', now(), 0, 'demo', 'demo',
+'c956de0f1cb49c302777ded5dd8bf85848b21ca1',
+'Demo', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true);
\ No newline at end of file
diff --git a/modules/core/src/app.properties b/modules/core/src/app.properties
new file mode 100644
index 0000000..4df2155
--- /dev/null
+++ b/modules/core/src/app.properties
@@ -0,0 +1,25 @@
+###############################################################################
+# Configuration #
+###############################################################################
+
+cuba.dbmsType = postgres
+
+cuba.springContextConfig = cuba-spring.xml spring.xml
+
+cuba.persistenceConfig = cuba-persistence.xml persistence.xml
+
+cuba.metadataConfig = cuba-metadata.xml metadata.xml
+
+cuba.viewsConfig = cuba-views.xml views.xml
+
+cuba.mainMessagePack = com.haulmont.cuba.core com.company.dashboard.core
+
+###############################################################################
+# Other #
+###############################################################################
+
+cuba.webContextName = dashboard-core
+cuba.availableLocales = English|en;Russian|ru
+cuba.localeSelectVisible = false
+cuba.restApiUrl = http://localhost:8082/dashboard-portal/api
+cuba.webAppUrl = http://localhost:8082/dashboard
diff --git a/modules/core/src/spring.xml b/modules/core/src/spring.xml
new file mode 100644
index 0000000..4ec4972
--- /dev/null
+++ b/modules/core/src/spring.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/modules/core/web/META-INF/context.xml b/modules/core/web/META-INF/context.xml
new file mode 100644
index 0000000..083ecb9
--- /dev/null
+++ b/modules/core/web/META-INF/context.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/modules/core/web/WEB-INF/web.xml b/modules/core/web/WEB-INF/web.xml
new file mode 100644
index 0000000..a7d1135
--- /dev/null
+++ b/modules/core/web/WEB-INF/web.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ appPropertiesConfig
+ classpath:cuba-app.properties
+ classpath:app.properties
+ /WEB-INF/local.app.properties
+ "file:${catalina.home}/conf/dashboard-core/local.app.properties"
+
+
+ com.haulmont.cuba.core.sys.AppContextLoader
+
+
+ remoting
+ com.haulmont.cuba.core.sys.remoting.RemotingServlet
+ 1
+
+
+ remoting
+ /remoting/*
+
+
\ No newline at end of file
diff --git a/modules/global/src/com/company/dashboard/route/Route.java b/modules/global/src/com/company/dashboard/route/Route.java
new file mode 100644
index 0000000..c27d150
--- /dev/null
+++ b/modules/global/src/com/company/dashboard/route/Route.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Haulmont
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.company.dashboard.route;
+
+import com.haulmont.chile.core.annotations.MetaClass;
+import com.haulmont.chile.core.annotations.MetaProperty;
+import com.haulmont.cuba.core.entity.AbstractNotPersistentEntity;
+
+@MetaClass(name = "demo$Route")
+public class Route extends AbstractNotPersistentEntity {
+ private static final long serialVersionUID = 121463765559033258L;
+
+ @MetaProperty
+ protected String time;
+
+ @MetaProperty
+ protected String arrival;
+
+ @MetaProperty
+ protected String departure;
+
+ public Route(String time, String departure, String arrival) {
+ this.time = time;
+ this.departure = departure;
+ this.arrival = arrival;
+ }
+
+ public void setTime(String time) {
+ this.time = time;
+ }
+
+ public String getTime() {
+ return time;
+ }
+
+ public void setArrival(String arrival) {
+ this.arrival = arrival;
+ }
+
+ public String getArrival() {
+ return arrival;
+ }
+
+ public void setDeparture(String departure) {
+ this.departure = departure;
+ }
+
+ public String getDeparture() {
+ return departure;
+ }
+}
\ No newline at end of file
diff --git a/modules/global/src/com/company/dashboard/route/messages.properties b/modules/global/src/com/company/dashboard/route/messages.properties
new file mode 100644
index 0000000..f93cebb
--- /dev/null
+++ b/modules/global/src/com/company/dashboard/route/messages.properties
@@ -0,0 +1,4 @@
+Route.departure = Departure
+Route.arrival = Arrival
+Route.time = Time
+Route = Route
diff --git a/modules/global/src/metadata.xml b/modules/global/src/metadata.xml
new file mode 100644
index 0000000..2b2f2b9
--- /dev/null
+++ b/modules/global/src/metadata.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ com.company.dashboard.route.Route
+
+
diff --git a/modules/global/src/persistence.xml b/modules/global/src/persistence.xml
new file mode 100644
index 0000000..acf0e15
--- /dev/null
+++ b/modules/global/src/persistence.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/modules/global/src/views.xml b/modules/global/src/views.xml
new file mode 100644
index 0000000..0add6e6
--- /dev/null
+++ b/modules/global/src/views.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/gui/src/com/company/dashboard/gui/messages.properties b/modules/gui/src/com/company/dashboard/gui/messages.properties
new file mode 100644
index 0000000..b5bd0e3
--- /dev/null
+++ b/modules/gui/src/com/company/dashboard/gui/messages.properties
@@ -0,0 +1,6 @@
+application.caption = CUBA Platform Dashboard Demo
+application.logoImage = branding/app-icon-menu.png
+
+loginWindow.caption = CUBA Platform Dashboard Demo
+loginWindow.welcomeLabel = Taxi Dashboard Demo
+loginWindow.logoImage = icons/car_black.png
diff --git a/modules/gui/src/screens.xml b/modules/gui/src/screens.xml
new file mode 100644
index 0000000..a753af5
--- /dev/null
+++ b/modules/gui/src/screens.xml
@@ -0,0 +1,18 @@
+
+
+
+
diff --git a/modules/web/src/com/company/dashboard/web/App.java b/modules/web/src/com/company/dashboard/web/App.java
new file mode 100644
index 0000000..300d906
--- /dev/null
+++ b/modules/web/src/com/company/dashboard/web/App.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Haulmont
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.company.dashboard.web;
+
+import com.haulmont.cuba.web.AppUI;
+import com.haulmont.cuba.web.DefaultApp;
+import com.haulmont.charts.web.gui.ChartComponentPalette;
+import com.haulmont.cuba.web.LoginWindow;
+import com.haulmont.cuba.web.UIView;
+import com.haulmont.cuba.web.gui.WebUIPaletteManager;
+
+public class App extends DefaultApp {
+ static {
+ WebUIPaletteManager.registerPalettes(new ChartComponentPalette());
+ }
+
+ @Override
+ protected UIView createLoginWindow(AppUI ui) {
+ return new LoginWindow(ui);
+ }
+}
\ No newline at end of file
diff --git a/modules/web/src/com/company/dashboard/web/bootstrap/BootstrapListener.java b/modules/web/src/com/company/dashboard/web/bootstrap/BootstrapListener.java
new file mode 100644
index 0000000..0303ae3
--- /dev/null
+++ b/modules/web/src/com/company/dashboard/web/bootstrap/BootstrapListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Haulmont
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.company.dashboard.web.bootstrap;
+
+import com.haulmont.cuba.web.sys.CubaBootstrapListener;
+import com.vaadin.server.BootstrapPageResponse;
+import org.jsoup.nodes.Element;
+
+public class BootstrapListener extends CubaBootstrapListener {
+
+ @Override
+ public void modifyBootstrapPage(BootstrapPageResponse response) {
+ super.modifyBootstrapPage(response);
+
+ Element head = response.getDocument().getElementsByTag("head").get(0);
+ includeMetaViewport("width=device-width, initial-scale=0.8", response, head);
+ }
+
+ protected void includeMetaViewport(String content, BootstrapPageResponse response, Element head) {
+ Element meta = response.getDocument().createElement("meta");
+ meta.attr("name", "viewport");
+ meta.attr("content", content);
+ head.appendChild(meta);
+ }
+}
diff --git a/modules/web/src/com/company/dashboard/web/mainwindow/ExtAppMainWindow.java b/modules/web/src/com/company/dashboard/web/mainwindow/ExtAppMainWindow.java
new file mode 100644
index 0000000..020d685
--- /dev/null
+++ b/modules/web/src/com/company/dashboard/web/mainwindow/ExtAppMainWindow.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2016 Haulmont
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.company.dashboard.web.mainwindow;
+
+import com.company.dashboard.route.Route;
+import com.haulmont.charts.gui.amcharts.model.charts.PieChart;
+import com.haulmont.charts.gui.amcharts.model.charts.SerialChart;
+import com.haulmont.charts.gui.amcharts.model.data.DataItem;
+import com.haulmont.charts.gui.amcharts.model.data.ListDataProvider;
+import com.haulmont.charts.gui.amcharts.model.data.MapDataItem;
+import com.haulmont.charts.gui.components.charts.Chart;
+import com.haulmont.charts.gui.components.map.MapViewer;
+import com.haulmont.charts.gui.map.model.GeoPoint;
+import com.haulmont.charts.gui.map.model.InfoWindow;
+import com.haulmont.charts.gui.map.model.Marker;
+import com.haulmont.charts.gui.map.model.base.MarkerImage;
+import com.haulmont.cuba.gui.ComponentsHelper;
+import com.haulmont.cuba.gui.components.*;
+import com.haulmont.cuba.gui.data.CollectionDatasource;
+import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
+import com.haulmont.cuba.web.Connection;
+import com.haulmont.cuba.web.WebWindowManager;
+
+import javax.inject.Inject;
+import java.util.Map;
+import java.util.UUID;
+
+public class ExtAppMainWindow extends AbstractMainWindow {
+
+ private InfoWindow openedCarTooltip = null;
+
+ @Inject
+ private Chart redLineChart;
+
+ @Inject
+ private Chart greenSerialChart;
+
+ @Inject
+ private Chart blueSerialChart;
+
+ @Inject
+ private Chart greenLineChart;
+
+ @Inject
+ private Chart pieChart;
+
+ @Inject
+ private Table routesTable;
+
+ @Inject
+ private ComponentsFactory componentsFactory;
+
+ @Inject
+ private MapViewer map;
+
+ @Inject
+ private CollectionDatasource routeDs;
+
+ @Override
+ public void init(Map params) {
+ super.init(params);
+
+ setChartData();
+ setPieChartData();
+ initMap();
+ initTable();
+ }
+
+ private void initTable() {
+ routeDs.addItem(new Route("00:11", "149 S 4th St", "202 S 14th St"));
+ routeDs.addItem(new Route("00:32", "1192 Fremont St", "1252 Randol Ave"));
+ routeDs.addItem(new Route("00:46", "760 Chapman St", "905 Morse St"));
+ routeDs.addItem(new Route("01:03", "986 Silicon Dr", "1042 Newhall St"));
+ routeDs.addItem(new Route("01:52", "1080 Portola Ave", "790 Locust St"));
+ routeDs.addItem(new Route("02:27", "1098 Lexington St", "1313 Franklin St"));
+
+ routesTable.addGeneratedColumn("direction", routes -> {
+ Label dataTypeLabel = componentsFactory.createComponent(Label.class);
+ dataTypeLabel.setValue("\u2192");
+ dataTypeLabel.setStyleName("direction");
+ return dataTypeLabel;
+ });
+
+ routesTable.setSettingsEnabled(false);
+ }
+
+ private void initMap() {
+ GeoPoint center = map.createGeoPoint(37.3359782, -121.924535);
+ map.setCenter(center);
+ map.setScrollWheelEnabled(false);
+ map.setDraggable(false);
+ map.setZoom(14);
+
+ Marker marker = map.createMarker("3C4PDCAB9FT553312\nPadillac Fanta Se", map.createGeoPoint(37.340380, -121.961185), true);
+ MarkerImage icon = map.createMarkerImage("VAADIN/themes/halo/car/taxi_60.svg");
+ marker.setIcon(icon);
+ marker.setClickable(true);
+ marker.setDraggable(false);
+ map.addMarker(marker);
+
+ marker = map.createMarker("1GCGK29R2XF085887\nDMW GP", map.createGeoPoint(37.334988, -121.915265), true);
+ icon = map.createMarkerImage("VAADIN/themes/halo/car/taxi_90.svg");
+ marker.setIcon(icon);
+ marker.setClickable(true);
+ marker.setDraggable(false);
+ map.addMarker(marker);
+
+ marker = map.createMarker("1FMCU4K32BKC11669\nFevrolet Fishdorado", map.createGeoPoint(37.333078, -121.961786), true);
+ icon = map.createMarkerImage("VAADIN/themes/halo/car/taxi_210.svg");
+ marker.setIcon(icon);
+ marker.setClickable(true);
+ marker.setDraggable(false);
+ map.addMarker(marker);
+
+ marker = map.createMarker("1GCHK34F6VE179779\nPulmmer P3", map.createGeoPoint(37.338332, -121.879474), true);
+ icon = map.createMarkerImage("VAADIN/themes/halo/car/taxi_300.svg");
+ marker.setIcon(icon);
+ marker.setClickable(true);
+ marker.setDraggable(false);
+ map.addMarker(marker);
+
+ marker = map.createMarker("1GCEK14T22Z280951\nPulmmer P3", map.createGeoPoint(37.330552, -121.929256), true);
+ marker.setIcon(icon);
+ marker.setClickable(true);
+ marker.setDraggable(false);
+ map.addMarker(marker);
+
+ map.addMarkerClickListener( e -> {
+ Marker clickedMarker = e.getMarker();
+ String caption = clickedMarker.getCaption();
+ caption = caption.replaceAll("\n", " ");
+ InfoWindow w = map.createInfoWindow(caption, clickedMarker);
+ if (this.openedCarTooltip != null) {
+ map.closeInfoWindow(this.openedCarTooltip);
+ }
+ map.openInfoWindow(w);
+ this.openedCarTooltip = w;
+ });
+
+ map.addInfoWindowClosedListener( e -> {
+ this.openedCarTooltip = null;
+ });
+ }
+
+ private void setChartData() {
+ ListDataProvider topRedLineDataProvider = new ListDataProvider();
+ ListDataProvider topGreenSerialDataProvider = new ListDataProvider();
+ ListDataProvider topBlueSerialDataProvider = new ListDataProvider();
+ ListDataProvider topGreenLineDataProvider = new ListDataProvider();
+
+ int[] redLineChartData = {5, 7, 6, 9, 7, 8, 5, 6, 4, 6, 5, 7, 4, 5, 3, 4, 2, 0};
+ for (int i = 0; i < redLineChartData.length; i++) {
+ topRedLineDataProvider.addItem(graphData(redLineChartData[i]));
+ }
+
+ SerialChart redLineChartConfiguration = (SerialChart) redLineChart.getConfiguration();
+ redLineChartConfiguration.setDataProvider(topRedLineDataProvider);
+
+ int[] greenSerialChartData = {
+ 3, 2, 5, 4, 3, 2, 1, 3, 3, 5,
+ 3, 3, 2, 3, 3, 5, 2, 2, 3, 1,
+ 5, 4, 3, 2, 1, 3, 3, 5, 3, 1};
+ int[] greenSerialChartDataBack = {
+ 8, 8, 5, 9, 7, 9, 7, 9, 9, 6,
+ 7, 9, 8, 9, 3, 5, 6, 8, 7, 9,
+ 7, 9, 3, 6, 1, 3, 6, 5, 9, 7};
+ for (int i = 0; i < greenSerialChartData.length; i++) {
+ topGreenSerialDataProvider.addItem(graphDataBack(greenSerialChartData[i], greenSerialChartDataBack[i]));
+ }
+
+ SerialChart greenSerialChartConfiguration = (SerialChart) greenSerialChart.getConfiguration();
+ greenSerialChartConfiguration.setDataProvider(topGreenSerialDataProvider);
+
+ int[] blueSerialChartData = {
+ 5, 4, 9, 8, 1, 9, 3, 5, 8, 8,
+ 5, 4, 9, 8, 1, 9, 3, 5, 3, 6,
+ 3, 9, 8, 9, 3, 5, 8, 2, 3, 6};
+ int[] blueSerialChartDataBack = {
+ 8, 8, 5, 9, 7, 9, 7, 9, 9, 6,
+ 7, 9, 8, 9, 3, 5, 6, 8, 7, 9,
+ 7, 9, 3, 6, 1, 3, 6, 5, 9, 7};
+ for (int i = 0; i < blueSerialChartData.length; i++) {
+ topBlueSerialDataProvider.addItem(graphDataBack(blueSerialChartData[i], blueSerialChartDataBack[i]));
+ }
+
+ SerialChart blueSerialChartConfiguration = (SerialChart) blueSerialChart.getConfiguration();
+ blueSerialChartConfiguration.setDataProvider(topBlueSerialDataProvider);
+
+ int[] greenLineChartData = {10, 8, 15, 28, 15, 18, 15, 23, 18, 12, 16, 10, 18};
+ for (int i = 0; i < greenLineChartData.length; i++) {
+ topGreenLineDataProvider.addItem(graphData(greenLineChartData[i]));
+ }
+
+ SerialChart greenLineChartConfiguration = (SerialChart) greenLineChart.getConfiguration();
+ greenLineChartConfiguration.setDataProvider(topGreenLineDataProvider);
+ }
+
+ private void setPieChartData() {
+ ListDataProvider dataProvider = new ListDataProvider();
+
+ dataProvider.addItem(pieCount(27, getMessage("late"), "#FDD400"));
+ dataProvider.addItem(pieCount(61, getMessage("inTime"), "#84B761"));
+ dataProvider.addItem(pieCount(12, getMessage("early"), "#67B7DC"));
+
+ PieChart pieChartVals = (PieChart) pieChart.getConfiguration();
+ pieChartVals.setDataProvider(dataProvider);
+ }
+
+ public void buttonNotImplementedAction() {
+ showNotification(getMessage("buttonMessage"), NotificationType.HUMANIZED);
+ }
+
+ private DataItem graphData(int value) {
+ MapDataItem item = new MapDataItem();
+ item.add("value", value);
+ return item;
+ }
+
+ private DataItem graphDataBack(int value, int backValue) {
+ MapDataItem item = new MapDataItem();
+ item.add("value", value);
+ item.add("backValue", backValue);
+ return item;
+ }
+
+ private DataItem pieCount(int carClassCount, String carClassName, String carColor) {
+ MapDataItem item = new MapDataItem();
+ item.add("class", carClassCount);
+ item.add("type", carClassName);
+ item.add("color", carColor);
+ return item;
+ }
+
+ public void signOut() {
+ Window window = ComponentsHelper.getWindow(this);
+ if (window == null) {
+ throw new IllegalStateException("Unable to find Frame for logout button");
+ }
+
+ window.saveSettings();
+
+ final WebWindowManager wm = (WebWindowManager) window.getWindowManager();
+ wm.checkModificationsAndCloseAll(() -> {
+ Connection connection = wm.getApp().getConnection();
+ connection.logout();
+ });
+ }
+}
\ No newline at end of file
diff --git a/modules/web/src/com/company/dashboard/web/mainwindow/ext-mainwindow.xml b/modules/web/src/com/company/dashboard/web/mainwindow/ext-mainwindow.xml
new file mode 100644
index 0000000..e2bf3ad
--- /dev/null
+++ b/modules/web/src/com/company/dashboard/web/mainwindow/ext-mainwindow.xml
@@ -0,0 +1,499 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+