├── .babelrc
├── .dockerignore
├── .gitignore
├── Dockerfile
├── Procfile
├── README.md
├── assets
├── javascripts
│ └── application.js
└── stylesheets
│ └── application.scss
├── config
└── config.exs
├── dashboards
├── elixir.html.eex
├── error.html.eex
├── jobs.html.eex
├── layout.html.eex
├── rotator.html.eex
├── sample.html.eex
└── sample1080.html.eex
├── elixir_buildpack.config
├── jobs
├── buzzwords.exs
├── convergence.exs
├── random.exs
├── reddit.exs
├── stats.exs
├── text.exs
├── travis.exs
├── twitter.exs
└── users.exs
├── lib
├── convergence.ex
├── demo.ex
├── reddit.ex
└── travis.ex
├── mix.exs
├── mix.lock
├── package.json
├── public
└── assets
│ └── favicon.ico
├── webpack.config.js
└── widgets
├── clock
├── clock.js
└── clock.scss
├── graph
├── graph.js
└── graph.scss
├── list
├── list.js
└── list.scss
├── meter
├── meter.js
└── meter.scss
├── number
├── number.js
└── number.scss
├── text
├── text.js
└── text.scss
├── time_took
├── time_took.js
└── time_took.scss
└── travis
├── travis.js
└── travis.scss
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | deps
3 | _build
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /cover
3 | /deps
4 | erl_crash.dump
5 | *.ez
6 |
7 | # Numerous always-ignore extensions
8 | *.diff
9 | *.err
10 | *.orig
11 | *.log
12 | *.rej
13 | *.swo
14 | *.swp
15 | *.vi
16 | *~
17 | *.sass-cache
18 |
19 | # OS or Editor files/dirs
20 | .DS_Store
21 | .cache
22 | .project
23 | .settings
24 | .tmproj
25 | nbproject
26 | Thumbs.db
27 |
28 | # NPM packages
29 | node_modules/
30 |
31 | # Compiled Assets
32 | public/*
33 | !public/assets
34 | public/assets/*
35 | !public/assets/favicon.*
36 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM zorbash/kitto
2 |
3 | ADD . /dashboard
4 | WORKDIR /dashboard
5 |
6 | ENV MIX_ENV prod
7 |
8 | RUN mix deps.get
9 | RUN npm install
10 | RUN npm run build
11 | RUN mix compile
12 |
13 | CMD mix kitto.server
14 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: mix kitto.server
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | This demo app is deployed at http://kitto.io
4 |
5 | The [heroku](https://github.com/kittoframework/demo/tree/heroku) branch of this repo is
6 | also automatically deployed to heroku at: https://kitto.herokuapp.com
7 |
8 | Example dashboards:
9 |
10 | * http://kitto.io/dashboards/sample
11 |
12 | 
13 |
14 | * http://kitto.io/dashboards/elixir
15 |
16 | 
17 |
18 | * http://kitto.io/dashboards/jobs
19 |
20 | 
21 |
22 | To start your Dashboard:
23 |
24 | * Install dependencies with `mix deps.get`
25 | * Install Node.js dependencies with `npm install`
26 | * Start a development server with `mix kitto.server`
27 |
28 | Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
29 |
30 | Ready to run in production? Please [check our deployment guides](https://github.com/kittoframework/kitto#deployment).
31 |
32 | ## Requirements
33 |
34 | For platform requirements see: [here](https://github.com/kittoframework/kitto#requirements).
35 |
36 | Some of the jobs in the demo make authenticated requests and have to be
37 | configured in order to work.
38 |
39 | ### Travis
40 |
41 | ```elixir
42 | # File: config/config.exs
43 | config :kitto, travis_token: "add-your-token"
44 | ```
45 |
46 | ### Twitter
47 |
48 | For available configuration options of the `extwitter` dependency,
49 | consult the
50 | [readme](https://github.com/parroty/extwitter/tree/v0.7.0#configuration).
51 |
52 | ```elixir
53 | # File: config/config.exs
54 | config :extwitter, :oauth,
55 | consumer_key: "your-consumer-key",
56 | consumer_secret: "your-consumer-secret",
57 | access_token: "your-access-token",
58 | access_token_secret: "your-access-token-secret"
59 | ```
60 |
61 | ## Learn more
62 |
63 | * Official website: http://kitto.io
64 | * Source: https://github.com/kittoframework/kitto
65 |
--------------------------------------------------------------------------------
/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | import '../stylesheets/application.scss';
2 |
3 | import $ from 'jquery';
4 | import {Kitto} from 'kitto';
5 |
6 | window.jQuery = window.$ = $;
7 |
8 | Kitto.start();
9 |
--------------------------------------------------------------------------------
/assets/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | @import "~jquery.gridster.css";
2 | @import '~font-awesome/css/font-awesome.css';
3 |
4 | // ----------------------------------------------------------------------------
5 | // Sass declarations
6 | // ----------------------------------------------------------------------------
7 | $background-color: #222;
8 | $text-color: #fff;
9 |
10 | $background-warning-color-1: #e82711;
11 | $background-warning-color-2: #9b2d23;
12 | $text-warning-color: #fff;
13 |
14 | $background-danger-color-1: #eeae32;
15 | $background-danger-color-2: #ff9618;
16 | $text-danger-color: #fff;
17 |
18 | @-webkit-keyframes status-warning-background {
19 | 0% { background-color: $background-warning-color-1; }
20 | 50% { background-color: $background-warning-color-2; }
21 | 100% { background-color: $background-warning-color-1; }
22 | }
23 | @-webkit-keyframes status-danger-background {
24 | 0% { background-color: $background-danger-color-1; }
25 | 50% { background-color: $background-danger-color-2; }
26 | 100% { background-color: $background-danger-color-1; }
27 | }
28 | @mixin animation($animation-name, $duration, $function, $animation-iteration-count:""){
29 | -webkit-animation: $animation-name $duration $function #{$animation-iteration-count};
30 | -moz-animation: $animation-name $duration $function #{$animation-iteration-count};
31 | -ms-animation: $animation-name $duration $function #{$animation-iteration-count};
32 | }
33 |
34 | // ----------------------------------------------------------------------------
35 | // Base styles
36 | // ----------------------------------------------------------------------------
37 | html {
38 | font-size: 100%;
39 | -webkit-text-size-adjust: 100%;
40 | -ms-text-size-adjust: 100%;
41 | }
42 |
43 | body {
44 | margin: 0;
45 | background-color: $background-color;
46 | font-size: 20px;
47 | color: $text-color;
48 | font-family: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
49 | }
50 |
51 | b, strong {
52 | font-weight: bold;
53 | }
54 |
55 | a {
56 | text-decoration: none;
57 | color: inherit;
58 | }
59 |
60 | img {
61 | border: 0;
62 | -ms-interpolation-mode: bicubic;
63 | vertical-align: middle;
64 | }
65 |
66 | img, object {
67 | max-width: 100%;
68 | }
69 |
70 | iframe {
71 | max-width: 100%;
72 | }
73 |
74 | table {
75 | border-collapse: collapse;
76 | border-spacing: 0;
77 | width: 100%;
78 | }
79 |
80 | td {
81 | vertical-align: middle;
82 | }
83 |
84 | ul, ol {
85 | padding: 0;
86 | margin: 0;
87 | }
88 |
89 | h1, h2, h3, h4, h5, p {
90 | padding: 0;
91 | margin: 0;
92 | }
93 | h1 {
94 | margin-bottom: 12px;
95 | text-align: center;
96 | font-size: 30px;
97 | font-weight: 400;
98 | }
99 | h2 {
100 | text-transform: uppercase;
101 | font-size: 76px;
102 | font-weight: 700;
103 | color: $text-color;
104 | }
105 | h3 {
106 | font-size: 25px;
107 | font-weight: 600;
108 | color: $text-color;
109 | }
110 |
111 | // ----------------------------------------------------------------------------
112 | // Base widget styles
113 | // ----------------------------------------------------------------------------
114 | .gridster {
115 | margin: 0px auto;
116 | }
117 |
118 | .icon-background {
119 | width: 100%!important;
120 | height: 100%;
121 | position: absolute;
122 | left: 0;
123 | top: 0;
124 | opacity: 0.1;
125 | font-size: 275px;
126 | text-align: center;
127 | margin-top: 82px;
128 | }
129 |
130 | .list-nostyle {
131 | list-style: none;
132 | }
133 |
134 | .gridster ul {
135 | list-style: none;
136 | }
137 |
138 | .gs-w {
139 | width: 100%;
140 | display: table;
141 | cursor: pointer;
142 | }
143 |
144 | .widget {
145 | display: table-cell;
146 | padding: 25px 12px;
147 | box-sizing: border-box;
148 | text-align: center;
149 | width: 100%;
150 | vertical-align: middle;
151 | }
152 |
153 | .widget.status-warning {
154 | background-color: $background-warning-color-1;
155 | @include animation(status-warning-background, 2s, ease, infinite);
156 |
157 | .icon-warning-sign {
158 | display: inline-block;
159 | }
160 |
161 | .title, .more-info {
162 | color: $text-warning-color;
163 | }
164 | }
165 |
166 | .widget.status-danger {
167 | color: $text-danger-color;
168 | background-color: $background-danger-color-1;
169 | @include animation(status-danger-background, 2s, ease, infinite);
170 |
171 | .icon-warning-sign {
172 | display: inline-block;
173 | }
174 |
175 | .title, .more-info {
176 | color: $text-danger-color;
177 | }
178 | }
179 |
180 | .more-info {
181 | font-size: 15px;
182 | position: absolute;
183 | bottom: 32px;
184 | left: 0;
185 | right: 0;
186 | }
187 |
188 | .updated-at {
189 | font-size: 15px;
190 | position: absolute;
191 | bottom: 12px;
192 | left: 0;
193 | right: 0;
194 | }
195 |
196 | #save-gridster {
197 | display: none;
198 | position: fixed;
199 | top: 0;
200 | margin: 0px auto;
201 | left: 50%;
202 | z-index: 1000;
203 | background: black;
204 | width: 190px;
205 | text-align: center;
206 | border: 1px solid white;
207 | border-top: 0px;
208 | margin-left: -95px;
209 | padding: 15px;
210 | }
211 |
212 | #save-gridster:hover {
213 | padding-top: 25px;
214 | }
215 |
216 | #saving-instructions {
217 | display: none;
218 | padding: 10px;
219 | width: 500px;
220 | height: 122px;
221 | z-index: 1000;
222 | background: white;
223 | top: 100px;
224 | color: black;
225 | font-size: 15px;
226 | padding-bottom: 4px;
227 |
228 | textarea {
229 | white-space: nowrap;
230 | width: 494px;
231 | height: 80px;
232 | }
233 | }
234 |
235 | #lean_overlay {
236 | position: fixed;
237 | z-index:100;
238 | top: 0px;
239 | left: 0px;
240 | height:100%;
241 | width:100%;
242 | background: #000;
243 | display: none;
244 | }
245 |
246 | #container {
247 | padding-top: 5px;
248 | }
249 |
250 | .widget-list.widget-reddit {
251 | background-color: #ff4500;
252 | }
253 |
254 | .widget-list.widget-job {
255 | vertical-align: top;
256 | }
257 |
258 |
259 | // ----------------------------------------------------------------------------
260 | // Clearfix
261 | // ----------------------------------------------------------------------------
262 | .clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
263 | .clearfix:after { clear: both; }
264 | .clearfix { zoom: 1; }
265 |
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | # This file is responsible for configuring your application
2 | # and its dependencies with the aid of the Mix.Config module.
3 | #
4 | # This configuration file is loaded before any dependency and
5 | # is restricted to this project.
6 | use Mix.Config
7 |
8 | config :kitto, root: Path.dirname(__DIR__),
9 | travis_token: System.get_env("TRAVIS_TOKEN"),
10 | google_analytics_id: System.get_env("GOOGLE_ANALYTICS_ID")
11 |
12 | config :extwitter, :oauth,
13 | consumer_key: System.get_env("TWITTER_CONSUMER_KEY"),
14 | consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET"),
15 | access_token: System.get_env("TWITTER_ACCESS_TOKEN"),
16 | access_token_secret: System.get_env("TWITTER_ACCESS_TOKEN_SECRET")
17 |
18 | #config :kitto, :watch_assets?, false
19 |
20 | # Use port: {:system, "PORT"} to have port port configurable via env variable
21 | # Example: `PORT=4444 mix kitto.server` will start the server on port 4444
22 |
23 | # Configures Elixir's Logger
24 | config :logger, :console,
25 | format: "$time $metadata[$level] $message\n",
26 | metadata: [:request_id]
27 |
--------------------------------------------------------------------------------
/dashboards/elixir.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
7 |
8 |
9 |
10 | -
11 |
17 |
18 |
19 | -
23 |
29 |
30 |
31 | -
35 |
40 |
41 |
42 | -
46 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/dashboards/error.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= code %> - <%= message %>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
36 |
37 |
38 |
39 |
40 |
<%= code %>
41 | <%= message %>
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/dashboards/jobs.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
7 |
12 |
13 |
14 | -
18 |
24 |
25 |
26 | -
30 |
35 |
36 |
37 | -
41 |
46 |
47 |
48 | -
52 |
58 |
59 |
60 | -
64 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/dashboards/layout.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dashboard
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 | <%= @template %>
22 |
23 |
24 |
25 |
26 |
27 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/dashboards/rotator.html.eex:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | <%= for dashboard <- @dashboards do %>
8 | <%= Kitto.View.render_template(dashboard) %>
9 | <% end %>
10 |
11 |
--------------------------------------------------------------------------------
/dashboards/sample.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
10 |
11 |
12 | -
16 |
22 |
23 |
24 | -
28 |
34 |
35 |
36 | -
40 |
46 |
47 |
48 | -
52 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/dashboards/sample1080.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
7 |
13 |
14 |
15 | -
19 |
25 |
26 |
27 | -
31 |
37 |
38 |
39 | -
43 |
49 |
50 |
51 | -
55 |
59 |
60 |
61 | -
65 |
71 |
72 |
73 | -
77 |
82 |
83 |
84 | -
88 |
92 |
93 |
94 | -
98 |
104 |
105 |
106 | -
110 |
115 |
116 |
117 | -
121 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/elixir_buildpack.config:
--------------------------------------------------------------------------------
1 | elixir_version=1.3.0
2 |
--------------------------------------------------------------------------------
/jobs/buzzwords.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :buzzwords, every: :second do
4 | random = fn -> :rand.uniform * 100 |> Float.round end
5 |
6 | list = ~w[synergy startup catalyst docker microservice container elixir react]
7 | |> Enum.map(fn (w) -> %{label: w, value: random.()} end)
8 | |> Enum.shuffle
9 |
10 | broadcast! %{items: list}
11 | end
12 |
--------------------------------------------------------------------------------
/jobs/convergence.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | {:ok, convergence} = Kitto.Jobs.Convergence.new
4 | points = &(&1 |> Kitto.Jobs.Convergence.points)
5 |
6 | job :convergence, every: {2, :seconds} do
7 | broadcast! %{points: convergence |> points.()}
8 | end
9 |
--------------------------------------------------------------------------------
/jobs/random.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :random, every: :second do
4 | broadcast! %{value: :rand.uniform * 100 |> Float.round}
5 | end
6 |
--------------------------------------------------------------------------------
/jobs/reddit.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :reddit, every: {2, :seconds} do
4 | subreddit = "elixir"
5 | max_posts = 5
6 |
7 | headers = [%{label: "posts", value: "score"}]
8 | items = Demo.Reddit.r(subreddit)["data"]["children"]
9 | |> Enum.take(max_posts)
10 | |> Enum.map(fn (%{"data" => post}) ->
11 | %{label: post["title"], value: post["score"]}
12 | end)
13 |
14 | broadcast! %{items: headers ++ items}
15 | end
16 |
--------------------------------------------------------------------------------
/jobs/stats.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :job_failures, every: :second do
4 | stats = Kitto.StatsServer.stats
5 |
6 | failures = stats
7 | |> Enum.map(fn ({name, m}) -> %{label: name, value: m[:failures]} end)
8 | |> Enum.sort(fn (a, b) -> a[:value] > b[:value] end)
9 | |> Enum.take(15)
10 |
11 | broadcast! %{items: failures}
12 | end
13 |
14 | job :job_avg_time, every: {500, :milliseconds} do
15 | stats = Kitto.StatsServer.stats
16 |
17 | metrics = stats
18 | |> Enum.map(fn ({name, m}) ->
19 | %{label: name, value: m[:avg_time_took] |> Float.round(3)}
20 | end)
21 | |> Enum.sort(fn (a, b) -> a[:value] > b[:value] end)
22 | |> Enum.take(15)
23 |
24 | broadcast! :job_avg_time_took, %{items: metrics}
25 | end
26 |
27 | job :jobs_running, every: {200, :milliseconds} do
28 | stats = Kitto.StatsServer.stats
29 | |> Enum.filter(fn ({_name, m}) ->
30 | (m[:times_completed] + m[:failures]) < m[:times_triggered]
31 | end)
32 | |> length
33 |
34 | broadcast! %{value: stats}
35 | end
36 |
37 | job :footprint, every: :second do
38 | mem = :erlang.memory[:processes_used] / 1024 / 1024 |> Float.round(3)
39 |
40 | broadcast! :processes_count, %{value: length(:erlang.processes)}
41 | broadcast! :memory_usage, %{value: mem}
42 | end
43 |
44 | job :uptime, every: :second do
45 | hours = ((:erlang.statistics(:wall_clock) |> elem(0)) / 1000 / 60.0 / 60.0)
46 | |> Float.round(3)
47 |
48 | broadcast! %{value: hours}
49 | end
50 |
--------------------------------------------------------------------------------
/jobs/text.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :text, every: {4, :seconds} do
4 | phrases = ["This is your shiny new dashboard", "Built on the Kitto Framework"]
5 |
6 | broadcast! %{text: (phrases |> Enum.shuffle |> Enum.take(1))}
7 | end
8 |
--------------------------------------------------------------------------------
/jobs/travis.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :travis, every: {4, :seconds} do
4 | repo_names = ~w[elixir-lang/elixir elixir-lang/plug elixir-lang/ex_doc
5 | elixir-ecto/ecto phoenixframework/phoenix hexpm/hex kittoframework/kitto
6 | rrrene/credo edgurgel/httpoison bitwalker/distillery rails/rails]
7 |
8 | statuses = fn (status) ->
9 | case status do
10 | 0 -> "success"
11 | 1 -> "failure"
12 | _ -> "unknown"
13 | end
14 | end
15 |
16 | builds = repo_names |> Enum.map(fn (repo) ->
17 | %{label: repo, value: Demo.Travis.repo(repo)["last_build_status"] |> statuses.()}
18 | end)
19 |
20 | broadcast! %{items: builds}
21 | end
22 |
--------------------------------------------------------------------------------
/jobs/twitter.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :twitter, every: {20, :seconds} do
4 | items = ExTwitter.search("elixir-lang", count: 2)
5 | |> Enum.map(fn (t) -> %{label: t.text, value: ""} end)
6 |
7 | broadcast! %{items: items}
8 | end
9 |
10 | job :twitter_linux do
11 | spawn fn ->
12 | for tweet <- ExTwitter.stream_filter(track: "linux") do
13 | broadcast! %{text: tweet.text}
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/jobs/users.exs:
--------------------------------------------------------------------------------
1 | use Kitto.Job.DSL
2 |
3 | job :users, every: :second do
4 | broadcast! %{value: Kitto.Notifier.connections |> Enum.count}
5 | end
6 |
--------------------------------------------------------------------------------
/lib/convergence.ex:
--------------------------------------------------------------------------------
1 | defmodule Kitto.Jobs.Convergence do
2 | def new, do: Agent.start(fn -> 0 end)
3 |
4 | def points(pid, n \\ 10), do: points(pid, n, [point(pid)])
5 | defp points(_, n, acc) when length(acc) == n, do: acc
6 | defp points(pid, n, acc), do: points(pid, n, acc ++ [point(pid)])
7 | defp point(pid), do: %{x: pid |> next_point, y: random()}
8 |
9 | defp next_point(pid) do
10 | pid |> Agent.get_and_update(fn(n) -> next = n + 1; {next, next} end)
11 | end
12 |
13 | defp random, do: :rand.uniform * 100 |> Float.round
14 | end
15 |
--------------------------------------------------------------------------------
/lib/demo.ex:
--------------------------------------------------------------------------------
1 | defmodule Demo do
2 | end
3 |
--------------------------------------------------------------------------------
/lib/reddit.ex:
--------------------------------------------------------------------------------
1 | defmodule Demo.Reddit do
2 | @base_url "https://www.reddit.com/"
3 |
4 | def r(topic) do
5 | %{body: body} = (@base_url <> "r/" <> topic <> ".json") |> HTTPoison.get!(%{})
6 |
7 | body |> Poison.decode!
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/travis.ex:
--------------------------------------------------------------------------------
1 | defmodule Demo.Travis do
2 | @base_url "https://api.travis-ci.org/"
3 | @token Application.get_env :kitto, :travis_token
4 |
5 | def repo(r) do
6 | %{body: body} = (@base_url <> "repos/" <> r) |> HTTPoison.get!(%{}, headers)
7 |
8 | body |> Poison.decode!
9 | end
10 |
11 | defp headers do
12 | [{"Authorization", "token #{@token}" }]
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Demo.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :demo,
6 | version: "0.0.1",
7 | elixir: "~> 1.3",
8 | build_embedded: Mix.env == :prod,
9 | start_permanent: Mix.env == :prod,
10 | deps: deps()]
11 | end
12 |
13 | # Configuration for the OTP application.
14 | #
15 | # Type `mix help compile.app` for more information.
16 | def application do
17 | [applications: [:logger, :kitto, :httpoison]]
18 | end
19 |
20 | # Specifies your project dependencies.
21 | #
22 | # Type `mix help deps` for examples and options.
23 | defp deps do
24 | [{:kitto, "0.5.2"},
25 | {:httpoison, "~> 0.9", override: true},
26 | {:poison, "3.0.0", override: true},
27 | {:extwitter, "~> 0.9"}]
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{
2 | "certifi": {:hex, :certifi, "1.0.0", "1c787a85b1855ba354f0b8920392c19aa1d06b0ee1362f9141279620a5be2039", [:rebar3], [], "hexpm"},
3 | "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
4 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
5 | "extwitter": {:hex, :extwitter, "0.9.1", "ce17b050f6139bbdfeafe100d3aa0b133e6f51cbe3f51d4879de0d28dba63d13", [:mix], [{:oauther, "~> 1.1", [hex: :oauther, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
6 | "fs": {:hex, :fs, "2.12.0", "ad631efacc9a5683c8eaa1b274e24fa64a1b8eb30747e9595b93bec7e492e25e", [:rebar3], []},
7 | "hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [repo: "hexpm", hex: :certifi, optional: false]}, {:idna, "4.0.0", [repo: "hexpm", hex: :idna, optional: false]}, {:metrics, "1.0.1", [repo: "hexpm", hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [repo: "hexpm", hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [repo: "hexpm", hex: :ssl_verify_fun, optional: false]}], "hexpm"},
8 | "httpoison": {:hex, :httpoison, "0.11.1", "d06c571274c0e77b6cc50e548db3fd7779f611fbed6681fd60a331f66c143a0b", [:mix], [{:hackney, "~> 1.7.0", [repo: "hexpm", hex: :hackney, optional: false]}], "hexpm"},
9 | "idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], [], "hexpm"},
10 | "kitto": {:hex, :kitto, "0.5.2", "f6ef75c60124d6ebb28ad289416b042e7bc0ed64afa56313f102cd963227885f", [:mix], [{:cowboy, "~> 1.0.0", [repo: "hexpm", hex: :cowboy, optional: false]}, {:fs, "~> 2.12.0", [repo: "hexpm", hex: :fs, optional: false]}, {:httpoison, "~> 0.10.0", [repo: "hexpm", hex: :httpoison, optional: false]}, {:plug, "~> 1.3.2", [repo: "hexpm", hex: :plug, optional: false]}, {:poison, "~> 3.0", [repo: "hexpm", hex: :poison, optional: false]}], "hexpm"},
11 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
12 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], [], "hexpm"},
13 | "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
14 | "oauth": {:git, "https://github.com/tim/erlang-oauth.git", "fca8163e8f7af867015e2413c4b03a4f8f4df0c9", []},
15 | "oauther": {:hex, :oauther, "1.1.1", "7d8b16167bb587ecbcddd3f8792beb9ec3e7b65c1f8ebd86b8dd25318d535752", [:mix], [], "hexpm"},
16 | "plug": {:hex, :plug, "1.3.4", "b4ef3a383f991bfa594552ded44934f2a9853407899d47ecc0481777fb1906f6", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [repo: "hexpm", hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [repo: "hexpm", hex: :mime, optional: false]}], "hexpm"},
17 | "poison": {:hex, :poison, "3.0.0", "625ebd64d33ae2e65201c2c14d6c85c27cc8b68f2d0dd37828fde9c6920dd131", [:mix], []},
18 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
19 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "0.0.1",
4 | "author": "Dimitris Zorbas",
5 | "description": "Dashboard Application",
6 | "scripts": {
7 | "start": "./node_modules/.bin/webpack-dev-server --progress --colors",
8 | "heroku-postbuild": "npm run build",
9 | "build": "./node_modules/.bin/webpack --config webpack.config.js"
10 | },
11 | "main": "index.js",
12 | "license": "MIT",
13 | "keywords": [
14 | "webpack"
15 | ],
16 | "engines": {
17 | "npm": "3.10.9",
18 | "node": "4.3.1"
19 | },
20 | "devDependencies": {
21 | "webpack-dev-server": "^1.14.1"
22 | },
23 | "dependencies": {
24 | "kitto": "file:deps/kitto",
25 | "webpack": "^1.12.13",
26 | "webpack-merge": "^0.15.0",
27 | "compression-webpack-plugin": "0.3.1",
28 | "babel-core": "^6.5.2",
29 | "babel-loader": "^6.2.2",
30 | "babel-preset-es2015": "6.18.0",
31 | "babel-preset-react": "^6.5.0",
32 | "babel-preset-react-hmre": "^1.1.0",
33 | "sass-loader": "^3.2.0",
34 | "url-loader": "^0.5.5",
35 | "file-loader": "^0.8.1",
36 | "style-loader": "^0.13.0",
37 | "script-loader": "^0.7.0",
38 | "imports-loader": "0.6.5",
39 | "expose-loader": "0.7.1",
40 | "css-loader": "^0.9.1",
41 | "node-sass": "^3.4.2",
42 | "react": "^0.14.7",
43 | "react-dom": "^0.14.7",
44 | "glob": "7.1.0",
45 | "font-awesome": "^4.3.0",
46 | "d3": "3.5.17",
47 | "gridster": "0.5.6",
48 | "imports": "^1.0.0",
49 | "rickshaw": "1.6.0",
50 | "jquery": "^3.0.0",
51 | "jquery-knob": "1.2.11",
52 | "fscreen": "^1.0.2"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/public/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kittoframework/demo/cd42644e4129a6da2b727f84c97c19c6b589fbec/public/assets/favicon.ico
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const glob = require('glob');
2 | const path = require('path');
3 | const merge = require('webpack-merge');
4 | const webpack = require('webpack');
5 |
6 | const TARGET = process.env.npm_lifecycle_event;
7 | const PATHS = {
8 | app: path.join(__dirname, 'assets/javascripts/application.js'),
9 | widgets: glob.sync('./widgets/**/*.js'),
10 | build: path.join(__dirname, 'public/assets'),
11 | gridster: path.join(__dirname, 'node_modules/gridster/dist'),
12 | d3: path.join(__dirname, 'node_modules/d3/d3.min.js'),
13 | rickshaw: path.join(__dirname, 'node_modules/rickshaw/rickshaw.js')
14 | };
15 |
16 | process.env.BABEL_ENV = TARGET;
17 |
18 | const common = {
19 | entry: {
20 | application: PATHS.app,
21 | widgets: PATHS.widgets
22 | },
23 | resolve: {
24 | extensions: ['', '.js', '.jsx', 'css', 'scss'],
25 | modulesDirectories: ['node_modules', PATHS.gridster],
26 | alias: {
27 | d3: PATHS.d3
28 | }
29 | },
30 | output: {
31 | path: PATHS.build,
32 | publicPath: '/assets/',
33 | filename: '[name].js'
34 | },
35 | module: {
36 | loaders: [
37 | { test: /\.css$/, loaders: ['style', 'css'] },
38 | { test: /\.scss$/, loaders: ['style', 'css', 'sass'] },
39 | { test: /\.jsx?$/, loaders: ['babel?cacheDirectory'] },
40 | {
41 | test: /\.(svg|png|jpe?g|gif)(\?\S*)?$/,
42 | loader: 'url?limit=1000&name=images/[name].[ext]'
43 | },
44 | {
45 | test: /\.(eot|woff|woff2|ttf)(\?\S*)?$/,
46 | loader: 'url?limit=1000&name=fonts/[name].[ext]'
47 | },
48 | {
49 | test: require.resolve('jquery-knob'),
50 | loader: "imports?require=>false,define=>false,this=>window"
51 | },
52 | {
53 | test: PATHS.d3,
54 | loader: "script"
55 | },
56 | {
57 | test: require.resolve('rickshaw'),
58 | loader: "script"
59 | }
60 | ]
61 | }
62 | };
63 |
64 | // Development Environment
65 | if (TARGET === 'start' || !TARGET) {
66 | module.exports = merge(common, {
67 | devtool: 'eval-source-map',
68 | devServer: {
69 | contentBase: PATHS.build,
70 |
71 | historyApiFallback: true,
72 | hot: true,
73 | inline: true,
74 | progress: true,
75 |
76 | // display only errors to reduce the amount of output
77 | stats: 'errors-only',
78 |
79 | // Binding address of webpack-dev-server
80 | // Read more: https://github.com/kittoframework/kitto/wiki/Customize-Asset-Watcher
81 | host: process.env.KITTO_ASSETS_HOST,
82 | port: process.env.KITTO_ASSETS_PORT
83 | },
84 | plugins: [new webpack.HotModuleReplacementPlugin()]
85 | });
86 | }
87 |
88 | // Production Environment
89 | if (TARGET === 'build') {
90 | var CompressionPlugin = require("compression-webpack-plugin");
91 |
92 | module.exports = merge(common, {
93 | plugins: [
94 | new webpack.optimize.UglifyJsPlugin({
95 | compress: {
96 | warnings: false,
97 | keep_fnames: true
98 | },
99 | mangle: {
100 | keep_fnames: true
101 | }
102 | }),
103 | new CompressionPlugin({
104 | asset: '[path].gz[query]',
105 | algorithm: 'gzip',
106 | test: /\.js$|\.html$/,
107 | verbose: true
108 | })
109 | ]
110 | });
111 | }
112 |
--------------------------------------------------------------------------------
/widgets/clock/clock.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Widget} from 'kitto';
3 |
4 | import './clock.scss';
5 |
6 | class Clock extends Widget {
7 | constructor(props) {
8 | super(props);
9 | this.state = Clock.dateTime()
10 | setInterval(this.update.bind(this), 500);
11 | }
12 | update() { this.setState(Clock.dateTime()); }
13 | render() {
14 | return (
15 |
16 |
{this.state.date}
17 | {this.state.time}
18 |
19 | );
20 | }
21 | static formatTime(i) { return i < 10 ? "0" + i : i; }
22 | static dateTime() {
23 | var today = new Date(),
24 | h = today.getHours(),
25 | m = today.getMinutes(),
26 | s = today.getSeconds(),
27 | m = Clock.formatTime(m),
28 | s = Clock.formatTime(s);
29 |
30 | return {
31 | time: (h + ":" + m + ":" + s),
32 | date: today.toDateString(),
33 | }
34 | }
35 | };
36 |
37 | Widget.mount(Clock);
38 | export default Clock;
39 |
--------------------------------------------------------------------------------
/widgets/clock/clock.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #dc5945;
5 |
6 | // ----------------------------------------------------------------------------
7 | // Widget-clock styles
8 | // ----------------------------------------------------------------------------
9 | .widget-clock {
10 | background-color: $background-color;
11 |
12 | .time {
13 | font-size: 55px;
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/widgets/graph/graph.js:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom';
2 | import React from 'react';
3 | import 'd3';
4 | import 'rickshaw';
5 | import {Kitto, Widget} from 'kitto';
6 |
7 | import './graph.scss';
8 |
9 | class Graph extends Widget {
10 | static get defaultProps() {
11 | return { graphType: 'area' };
12 | }
13 |
14 | componentDidMount() {
15 | this.$node = $(ReactDOM.findDOMNode(this));
16 | this.current = 0;
17 | this.renderGraph();
18 | }
19 | renderGraph() {
20 | let container = this.$node.parent();
21 | let $gridster = $('.gridster');
22 | let config = Kitto.config();
23 | let widget_base_dimensions = config.widget_base_dimensions;
24 | let width = (widget_base_dimensions[0] *
25 | container.data('sizex')) + 5 * 2 * (container.data('sizex') - 1);
26 | let height = (widget_base_dimensions[1] * container.data('sizey'));
27 |
28 | this.graph = new Rickshaw.Graph({
29 | element: this.$node[0],
30 | width: width,
31 | height: height,
32 | renderer: this.props.graphType,
33 | series: [{color: '#fff', data: [{ x: 0, y: 0 }]}]
34 | });
35 |
36 | new Rickshaw.Graph.Axis.Time({ graph: this.graph });
37 | new Rickshaw.Graph.Axis.Y({ graph: this.graph,
38 | tickFormat: Rickshaw.Fixtures.Number.formatKMBT });
39 | this.graph.render();
40 | }
41 | componentWillUpdate(_props, state) {
42 | this.graph.series[0].data = state.points;
43 | this.current = state.points[state.points.length -1].y;
44 | this.graph.render();
45 | }
46 | currentValue() {
47 | return this.prettyNumber(this.prepend(this.current));
48 | }
49 | render() {
50 | return (
51 |
52 |
{this.props.title}
53 |
{this.currentValue()}
54 |
{this.props.moreinfo}
55 |
56 | );
57 | }
58 | };
59 |
60 | Widget.mount(Graph);
61 | export default Graph;
62 |
--------------------------------------------------------------------------------
/widgets/graph/graph.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #dc5945;
5 |
6 | $title-color: rgba(255, 255, 255, 0.7);
7 | $moreinfo-color: rgba(255, 255, 255, 0.3);
8 | $tick-color: rgba(0, 0, 0, 0.4);
9 |
10 |
11 | // ----------------------------------------------------------------------------
12 | // Widget-graph styles
13 | // ----------------------------------------------------------------------------
14 | .widget-graph {
15 |
16 | background-color: $background-color;
17 | position: relative;
18 |
19 |
20 | svg {
21 | position: absolute;
22 | opacity: 0.4;
23 | fill-opacity: 0.4;
24 | left: 0px;
25 | top: 0px;
26 | }
27 |
28 | .title, .value {
29 | position: relative;
30 | z-index: 99;
31 | }
32 |
33 | .title {
34 | color: $title-color;
35 | }
36 |
37 | .more-info {
38 | color: $moreinfo-color;
39 | font-weight: 600;
40 | font-size: 20px;
41 | margin-top: 0;
42 | }
43 |
44 | .x_tick {
45 | position: absolute;
46 | bottom: 0;
47 | .title {
48 | font-size: 20px;
49 | color: $tick-color;
50 | opacity: 0.5;
51 | padding-bottom: 3px;
52 | }
53 | }
54 |
55 | .y_ticks {
56 | font-size: 20px;
57 | fill: $tick-color;
58 | fill-opacity: 1;
59 | }
60 |
61 | .domain {
62 | display: none;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/widgets/list/list.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Widget, Helpers} from 'kitto';
3 |
4 | import './list.scss';
5 |
6 | class ListItem extends React.Component {
7 | render() {
8 | return (
9 |
10 |
11 | {Helpers.truncate(this.props.label, this.props.labelLength || 80)}
12 |
13 |
14 | {Helpers.truncate(this.props.value, this.props.valueLength)}
15 |
16 |
17 | );
18 | }
19 | }
20 |
21 | export class List extends Widget {
22 | renderItems(items) {
23 | return items.map((item, i) => {
24 | return ;
29 | });
30 | }
31 | renderList(items) {
32 | return this.props.unordered ? : {items}
;
33 | }
34 | render() {
35 | return (
36 |
37 |
{this.props.title}
38 |
{this.props.text}
39 |
40 | {this.renderList(this.renderItems(this.state.items || []))}
41 |
42 |
{this.props.moreinfo}
43 |
{this.updatedAt(this.state.updated_at)}
44 |
45 | );
46 | }
47 | };
48 |
49 | Widget.mount(List);
50 | export default List;
51 |
--------------------------------------------------------------------------------
/widgets/list/list.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #12b0c5;
5 | $value-color: #fff;
6 |
7 | $title-color: rgba(255, 255, 255, 0.7);
8 | $label-color: rgba(255, 255, 255, 0.7);
9 | $moreinfo-color: rgba(255, 255, 255, 0.7);
10 |
11 | // ----------------------------------------------------------------------------
12 | // Widget-list styles
13 | // ----------------------------------------------------------------------------
14 | .widget-list {
15 |
16 | background-color: $background-color;
17 |
18 | .title {
19 | color: $title-color;
20 | }
21 |
22 | ol, ul {
23 | margin: 0 15px;
24 | text-align: left;
25 | color: $label-color;
26 | }
27 |
28 | ol {
29 | list-style-position: inside;
30 | }
31 |
32 | li {
33 | margin-bottom: 5px;
34 | }
35 |
36 | .list-nostyle {
37 | list-style: none;
38 | }
39 |
40 | .label {
41 | color: $label-color;
42 | }
43 |
44 | .value {
45 | float: right;
46 | margin-left: 12px;
47 | font-weight: 600;
48 | color: $value-color;
49 | }
50 |
51 | .updated-at {
52 | color: rgba(0, 0, 0, 0.3);
53 | }
54 |
55 | .more-info {
56 | color: $moreinfo-color;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/widgets/meter/meter.js:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom';
2 | import React from 'react';
3 | import Knob from 'jquery-knob';
4 | import {Widget} from 'kitto';
5 |
6 | import './meter.scss';
7 |
8 | class Meter extends Widget {
9 | componentDidMount() {
10 | this.state = { value: 0 };
11 | this.$node = $(ReactDOM.findDOMNode(this));
12 | this.$meter = this.$node.find('.meter');
13 | this.$meter.attr('data-bgcolor', this.$meter.css('background-color'));
14 | this.$meter.attr('data-fgcolor', this.$meter.css('color'));
15 | this.$meter.knob();
16 | }
17 | componentDidUpdate() {
18 | this.$meter.val(this.state.value);
19 | this.$meter.trigger('change');
20 | }
21 | render() {
22 | return (
23 |
24 |
{this.props.title}
25 |
33 |
{this.updatedAt(this.state.updated_at)}
34 |
35 | );
36 | }
37 | };
38 |
39 | Widget.mount(Meter);
40 | export default Meter;
41 |
--------------------------------------------------------------------------------
/widgets/meter/meter.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #9c4274;
5 |
6 | $title-color: rgba(255, 255, 255, 0.7);
7 | $moreinfo-color: rgba(255, 255, 255, 0.3);
8 |
9 | $meter-background: darken($background-color, 15%);
10 |
11 | // ----------------------------------------------------------------------------
12 | // Widget-meter styles
13 | // ----------------------------------------------------------------------------
14 | .widget-meter {
15 |
16 | background-color: $background-color;
17 |
18 | input.meter {
19 | background-color: $meter-background;
20 | color: #fff;
21 | }
22 |
23 | .title {
24 | color: $title-color;
25 | }
26 |
27 | .more-info {
28 | color: $moreinfo-color;
29 | }
30 |
31 | .updated-at {
32 | color: rgba(0, 0, 0, 0.3);
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/widgets/number/number.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Widget} from 'kitto';
3 |
4 | import './number.scss';
5 |
6 | class Number extends Widget {
7 | constructor(props) {
8 | super(props);
9 |
10 | this.state = { value: 0 };
11 | this.lastValue = 0;
12 | }
13 | componentWillUpdate(_props, lastState) {
14 | this.lastValue = this.state.value;
15 | }
16 | decorateValue(value) {
17 | let number = this.shortenedNumber(this.state.value);
18 |
19 | return this.append(this.prepend(number, this.props.prefix), this.props.suffix);
20 | }
21 | arrow() {
22 | if (this.state.value > this.lastValue) {
23 | return ();
24 | } else {
25 | return ();
26 | }
27 | }
28 | difference() {
29 | if (this.lastValue && this.lastValue !== 0) {
30 | let normalized = (this.state.value - this.lastValue) / this.lastValue * 100;
31 | return `${Math.abs(Math.round(normalized))}%`
32 | } else {
33 | return '';
34 | }
35 | }
36 | changeRate() {
37 | if (this.props.changerate == "off") { return; }
38 |
39 | return (
40 |
41 | {this.arrow()}{this.difference()}
42 |
43 | );
44 | }
45 | render() {
46 | return (
47 |
48 |
{this.props.title}
49 |
{this.decorateValue(this.state.value)}
50 |
{this.props.moreinfo}
51 | {this.changeRate()}
52 |
{this.updatedAt(this.state.updated_at)}
53 |
54 | );
55 | }
56 | };
57 |
58 | Widget.mount(Number);
59 | export default Number;
60 |
--------------------------------------------------------------------------------
/widgets/number/number.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #47bbb3;
5 | $value-color: #fff;
6 |
7 | $title-color: rgba(255, 255, 255, 0.7);
8 | $moreinfo-color: rgba(255, 255, 255, 0.7);
9 |
10 | // ----------------------------------------------------------------------------
11 | // Widget-number styles
12 | // ----------------------------------------------------------------------------
13 | .widget-number {
14 |
15 | background-color: $background-color;
16 |
17 | .title {
18 | color: $title-color;
19 | }
20 |
21 | .value {
22 | color: $value-color;
23 | }
24 |
25 | .change-rate {
26 | font-weight: 500;
27 | font-size: 30px;
28 | color: $value-color;
29 | }
30 |
31 | .more-info {
32 | color: $moreinfo-color;
33 | }
34 |
35 | .updated-at {
36 | color: rgba(0, 0, 0, 0.3);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/widgets/text/text.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Widget} from 'kitto';
3 |
4 | import './text.scss';
5 |
6 | class Text extends Widget {
7 | status() {
8 | if (!this.state.status) { return ""; }
9 |
10 | return`status-${this.state.status}`;
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
{this.state.title || this.props.title}
17 |
{this.state.text || this.props.text}
18 |
{this.state.moreinfo || this.props.moreinfo}
19 |
{this.updatedAt(this.state.updated_at)}
20 |
21 | );
22 | }
23 | };
24 |
25 | Widget.mount(Text);
26 | export default Text;
27 |
--------------------------------------------------------------------------------
/widgets/text/text.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #ec663c;
5 |
6 | $title-color: rgba(255, 255, 255, 0.7);
7 | $moreinfo-color: rgba(255, 255, 255, 0.7);
8 |
9 | // ----------------------------------------------------------------------------
10 | // Widget-text styles
11 | // ----------------------------------------------------------------------------
12 | .widget-text {
13 |
14 | background-color: $background-color;
15 |
16 | .title {
17 | color: $title-color;
18 | }
19 |
20 | .more-info {
21 | color: $moreinfo-color;
22 | }
23 |
24 | .updated-at {
25 | color: rgba(255, 255, 255, 0.7);
26 | }
27 |
28 |
29 | &.large h3 {
30 | font-size: 65px;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/widgets/time_took/time_took.js:
--------------------------------------------------------------------------------
1 | import './time_took.scss';
2 |
--------------------------------------------------------------------------------
/widgets/time_took/time_took.scss:
--------------------------------------------------------------------------------
1 | .widget-list {
2 | &.time-took {
3 | background-color: #9c4274;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/widgets/travis/travis.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Widget} from 'kitto';
3 |
4 | import './travis.scss';
5 |
6 | class ListItem extends React.Component {
7 | render() {
8 | return (
9 |
10 | {this.props.label}
11 | ●
12 |
13 | );
14 | }
15 | }
16 |
17 | Widget.mount(class Travis extends Widget {
18 | renderItems(items) {
19 | return items.map((item, i) => {
20 | return ;
21 | });
22 | }
23 | renderList(items) {
24 | return this.props.unordered ? : {items}
;
25 | }
26 | render() {
27 | return (
28 |
29 |
{this.props.title}
30 |
{this.props.text}
31 |
32 | {this.renderList(this.renderItems(this.state.items || []))}
33 |
34 |
{this.updatedAt(this.state.updated_at)}
35 |
36 | );
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/widgets/travis/travis.scss:
--------------------------------------------------------------------------------
1 | // ----------------------------------------------------------------------------
2 | // Sass declarations
3 | // ----------------------------------------------------------------------------
4 | $background-color: #f1f1f1;
5 | $value-color: #fff;
6 |
7 | $title-color: #666;
8 | $label-color: #555;
9 |
10 | // ----------------------------------------------------------------------------
11 | // Widget-list styles
12 | // ----------------------------------------------------------------------------
13 | .widget-travis {
14 | vertical-align: top;
15 | background-color: $background-color;
16 |
17 | .title {
18 | color: $title-color;
19 | }
20 |
21 | ol, ul {
22 | margin: 0 15px;
23 | text-align: left;
24 | color: $label-color;
25 | }
26 |
27 | ol {
28 | list-style-position: inside;
29 | }
30 |
31 | li {
32 | margin-bottom: 5px;
33 | }
34 |
35 | .list-nostyle {
36 | list-style: none;
37 | }
38 |
39 | .label {
40 | color: $label-color;
41 | font-size: 13px;
42 | }
43 |
44 | .value {
45 | float: right;
46 | margin-left: 12px;
47 | font-weight: 600;
48 | color: $value-color;
49 | font-size: 20px;
50 | }
51 |
52 | .success {
53 | @extend .value;
54 | color: green;
55 | }
56 |
57 | .failure{
58 | @extend .value;
59 | color: red;
60 | }
61 |
62 | .unknown{
63 | @extend .value;
64 | color: gray;
65 | }
66 |
67 | .updated-at {
68 | color: rgba(0, 0, 0, 0.3);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------