analytic 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,8 +3,40 @@
3
3
  @tailwind utilities;
4
4
 
5
5
  @layer components {
6
+ .stat {
7
+ @apply flex gap-2 justify-between items-center;
8
+ }
9
+
10
+ .delta {
11
+ @apply font-normal flex gap-2 items-center;
12
+ }
13
+
14
+ .delta--neutral {
15
+ @apply text-slate-400;
16
+ }
17
+
18
+ .delta--positive {
19
+ @apply text-emerald-600;
20
+ }
21
+
22
+ .delta--negative {
23
+ @apply text-rose-600;
24
+ }
25
+
26
+ .pills {
27
+ @apply inline-flex gap-2 bg-slate-100 border-slate-200 rounded-full text-slate-600;
28
+ }
29
+
6
30
  .pill {
7
- @apply bg-slate-200 text-slate-800 hover:text-white hover:bg-indigo-600 rounded-full px-3 py-2;
31
+ @apply flex items-center justify-center gap-2 rounded-full px-6 py-2;
32
+ }
33
+
34
+ .pill--default {
35
+ @apply bg-slate-100 font-medium hover:bg-slate-200 hover:text-slate-800;
36
+ }
37
+
38
+ .pill--active {
39
+ @apply bg-white shadow font-semibold text-slate-800;
8
40
  }
9
41
 
10
42
  .card {
@@ -20,7 +52,7 @@
20
52
  }
21
53
 
22
54
  .card__content {
23
- @apply px-5 py-4
55
+ @apply px-5 py-4;
24
56
  }
25
57
 
26
58
  .card__title {
@@ -28,7 +60,11 @@
28
60
  }
29
61
 
30
62
  .card__value {
31
- @apply uppercase text-slate-800 font-extrabold text-lg;
63
+ @apply text-slate-800 font-extrabold text-lg;
64
+ }
65
+
66
+ .cards {
67
+ @apply grid gap-4 grid-cols-1 md:grid-cols-3;
32
68
  }
33
69
 
34
70
  .table {
@@ -1,25 +1,28 @@
1
1
  <%- provide(:title, @dashboard.name) %>
2
2
 
3
3
  <div class="space-y-4">
4
- <div class="flex gap-2">
5
- <%= link_to 'Today', analytic.dashboard_path(period: 'today'), class: 'pill' %>
6
- <%= link_to 'Yesterday', analytic.dashboard_path(period: 'yesterday'), class: 'pill' %>
7
- <%= link_to 'Week', analytic.dashboard_path(period: 'week'), class: 'pill' %>
8
- <%= link_to 'Month', analytic.dashboard_path(period: 'month'), class: 'pill' %>
9
- <%= link_to 'Year', analytic.dashboard_path(period: 'year'), class: 'pill' %>
4
+ <div class="pills">
5
+ <%= link_to 'All', analytic.dashboard_path, class: "pill #{@dashboard.period.nil? ? 'pill--active' : 'pill--default'}" %>
6
+ <%= link_to '24 hours', analytic.dashboard_path(period: '24h'), class: "pill #{@dashboard.period.eql?('24h') ? 'pill--active' : 'pill--default'}" %>
7
+ <%= link_to '7 days', analytic.dashboard_path(period: '7d'), class: "pill #{@dashboard.period.eql?('7d') ? 'pill--active' : 'pill--default'}" %>
8
+ <%= link_to '4 weeks', analytic.dashboard_path(period: '4w'), class: "pill #{@dashboard.period.eql?('4w') ? 'pill--active' : 'pill--default'}" %>
9
+ <%= link_to '12 months', analytic.dashboard_path(period: '12m'), class: "pill #{@dashboard.period.eql?('12m') ? 'pill--active' : 'pill--default'}" %>
10
10
  </div>
11
11
 
12
- <div class="grid grid-flow-col justify-stretch gap-4">
12
+ <div class="cards">
13
13
  <div class="card">
14
14
  <div class="card__header">
15
15
  <div class="card__title">
16
- <%= tag.i class: "fa-solid fa-users" %>
16
+ <%= fa_icon_tag("fa-solid fa-users") %>
17
17
  Visitors
18
18
  </div>
19
19
  </div>
20
20
  <div class="card__content">
21
21
  <div class="card__value">
22
- <%= number_with_delimiter @dashboard.distinct_visitors_count %>
22
+ <div class="stat">
23
+ <%= number_with_delimiter @dashboard.visitors.count %>
24
+ <%= delta_tag(@dashboard.visitors.delta) %>
25
+ </div>
23
26
  </div>
24
27
  </div>
25
28
  </div>
@@ -27,13 +30,16 @@
27
30
  <div class="card">
28
31
  <div class="card__header">
29
32
  <div class="card__title">
30
- <%= tag.i class: "fa-solid fa-globe" %>
33
+ <%= fa_icon_tag("fa-solid fa-globe") %>
31
34
  Sessions
32
35
  </div>
33
36
  </div>
34
37
  <div class="card__content">
35
38
  <div class="card__value">
36
- <%= number_with_delimiter @dashboard.distinct_sessions_count %>
39
+ <div class="stat">
40
+ <%= number_with_delimiter @dashboard.sessions.count %>
41
+ <%= delta_tag(@dashboard.sessions.delta) %>
42
+ </div>
37
43
  </div>
38
44
  </div>
39
45
  </div>
@@ -41,13 +47,16 @@
41
47
  <div class="card">
42
48
  <div class="card__header">
43
49
  <div class="card__title">
44
- <%= tag.i class: "fa-solid fa-eye" %>
50
+ <%= fa_icon_tag("fa-solid fa-eye") %>
45
51
  Views
46
52
  </div>
47
53
  </div>
48
54
  <div class="card__content">
49
55
  <div class="card__value">
50
- <%= number_with_delimiter @dashboard.count %>
56
+ <div class="stat">
57
+ <%= number_with_delimiter @dashboard.views.count %>
58
+ <%= delta_tag(@dashboard.views.delta) %>
59
+ </div>
51
60
  </div>
52
61
  </div>
53
62
  </div>
@@ -70,7 +79,7 @@
70
79
  </tr>
71
80
  </thead>
72
81
  <tbody>
73
- <%- @dashboard.pages.each do |(host, path, views)| -%>
82
+ <%- @dashboard.current.pages.each do |(host, path, views)| -%>
74
83
  <tr>
75
84
  <td><%= host %></td>
76
85
  <td><%= path %></td>
@@ -81,4 +90,42 @@
81
90
  </table>
82
91
  </div>
83
92
  </div>
93
+
94
+ <div class="cards">
95
+ <div class="card">
96
+ <div class="card__header">
97
+ <div class="card__title">
98
+ <%= tag.i class: "fa-solid fa-file-code" %>
99
+ Visitors
100
+ </div>
101
+ </div>
102
+ <div class="card__content">
103
+ <%= react(component: "Chart") %>
104
+ </div>
105
+ </div>
106
+
107
+ <div class="card">
108
+ <div class="card__header">
109
+ <div class="card__title">
110
+ <%= tag.i class: "fa-solid fa-globe" %>
111
+ Sessions
112
+ </div>
113
+ </div>
114
+ <div class="card__content">
115
+ <%= react(component: "Chart") %>
116
+ </div>
117
+ </div>
118
+
119
+ <div class="card">
120
+ <div class="card__header">
121
+ <div class="card__title">
122
+ <%= tag.i class: "fa-solid fa-eye" %>
123
+ Views
124
+ </div>
125
+ </div>
126
+ <div class="card__content">
127
+ <%= react(component: "Chart") %>
128
+ </div>
129
+ </div>
130
+ </div>
84
131
  </div>
@@ -6,7 +6,7 @@
6
6
  <%= csp_meta_tag %>
7
7
  <%= javascript_include_tag 'analytic/application' %>
8
8
  <%= stylesheet_link_tag 'analytic/application' %>
9
- <%= %>
9
+ <%= favicon_link_tag 'analytic/icon.svg', type: 'image/svg+xml' %>
10
10
  </head>
11
11
  <body class="bg-slate-50">
12
12
 
@@ -32,11 +32,5 @@
32
32
  <main class="container mx-auto px-4 py-8">
33
33
  <%= yield %>
34
34
  </main>
35
-
36
- <footer class="container mx-auto px-4 text-center">
37
- <div class="flex gap-2 justify-center">
38
- <%= time_tag(Time.current) %>
39
- </div>
40
- </footer>
41
35
  </body>
42
36
  </html>
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class CreateAnalyticViews < ActiveRecord::Migration[7.1]
3
+ class CreateAnalyticEvents < ActiveRecord::Migration[7.1]
4
4
  def change
5
- create_table :analytic_views do |t|
5
+ create_table :analytic_events do |t|
6
6
  t.uuid :visitor_id, null: false
7
7
  t.uuid :session_id, null: false
8
8
  t.inet :ip, null: false
@@ -2,19 +2,51 @@
2
2
 
3
3
  module Analytic
4
4
  class Config
5
- # @return [String]
6
- attr_accessor :timezone
5
+ # @example
6
+ # config.time_zone = ActiveSupport::TimeZone['Tokyo']
7
+ #
8
+ # @!attribute [rw] time_zone
9
+ # @return [ActiveSupport::TimeZone]
10
+ attr_accessor :time_zone
7
11
 
8
- # @return [Integer]
12
+ # @example
13
+ # config.ip_v4_mask = 24
14
+ #
15
+ # @!attribute [rw] ip_v4_mask
16
+ # @return [Integer]
9
17
  attr_accessor :ip_v4_mask
10
18
 
11
- # @return [Integer]
19
+ # @example
20
+ # config.ip_v6_mask = 48
21
+ #
22
+ # @!attribute [rw] ip_v6_mask
23
+ # @return [Integer]
12
24
  attr_accessor :ip_v6_mask
13
25
 
26
+ # @example
27
+ # config.connects_to = database: { writing: :primary, reading: :replica }
28
+ #
29
+ # @!attribute [rw] connects_to
30
+ # @return [Hash, nil]
31
+ attr_accessor :connects_to
32
+
33
+ # @!attribute [rw] middleware
34
+ # @return [Array<Rack::Middleware>]
35
+ attr_accessor :middleware
36
+
37
+ # @example
38
+ # config.params = %i[utm_source utm_medium utm_campaign utm_content utm_term ref source]
39
+ #
40
+ # @!attribute [rw] params
41
+ # @return [Array<Symbol>]
42
+ attr_accessor :params
43
+
14
44
  def initialize
15
- @timezone = Time.zone
45
+ @time_zone = Time.zone
16
46
  @ip_v4_mask = 24 # e.g. 255.255.255.255 => '255.255.255.0/255.255.255.0'
17
47
  @ip_v6_mask = 48 # e.g. 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' => 'ffff:ffff:ffff:0000:0000:0000:0000:0000'
48
+ @middleware = []
49
+ @params = %i[utm_source utm_medium utm_campaign utm_content utm_term ref source]
18
50
  end
19
51
 
20
52
  # @return [Boolean]
@@ -26,5 +58,15 @@ module Analytic
26
58
  def ip_v6_mask?
27
59
  @ip_v6_mask.present?
28
60
  end
61
+
62
+ # @return [Boolean]
63
+ def connects_to?
64
+ @connects_to.present?
65
+ end
66
+
67
+ # @param middleware [Rack::Middleware]
68
+ def use(*args, &block)
69
+ middleware << [args, block]
70
+ end
29
71
  end
30
72
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Analytic
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: analytic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-06 00:00:00.000000000 Z
11
+ date: 2024-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -34,10 +34,8 @@ files:
34
34
  - LICENSE
35
35
  - README.md
36
36
  - Rakefile
37
- - app/assets/builds/analytic/application.css
38
- - app/assets/builds/analytic/application.js
39
- - app/assets/builds/analytic/application.js.map
40
37
  - app/assets/config/analytic_manifest.js
38
+ - app/assets/images/analytic/icon.svg
41
39
  - app/controllers/analytic/application_controller.rb
42
40
  - app/controllers/analytic/dashboard_controller.rb
43
41
  - app/controllers/concerns/analytic/trackable.rb
@@ -47,7 +45,11 @@ files:
47
45
  - app/mailers/analytic/application_mailer.rb
48
46
  - app/models/analytic/application_record.rb
49
47
  - app/models/analytic/dashboard.rb
50
- - app/models/analytic/view.rb
48
+ - app/models/analytic/event.rb
49
+ - app/models/analytic/period.rb
50
+ - app/models/analytic/stat.rb
51
+ - app/packs/analytic/application/components/chart.tsx
52
+ - app/packs/analytic/application/components/index.tsx
51
53
  - app/packs/analytic/application/index.ts
52
54
  - app/packs/analytic/application/initializers/fontawesome.ts
53
55
  - app/packs/analytic/application/initializers/index.ts
@@ -58,7 +60,7 @@ files:
58
60
  - bin/dev
59
61
  - bin/rails
60
62
  - config/routes.rb
61
- - db/migrate/20240805210911_create_analytic_views.rb
63
+ - db/migrate/20240805210911_create_analytic_events.rb
62
64
  - lib/analytic.rb
63
65
  - lib/analytic/config.rb
64
66
  - lib/analytic/engine.rb
@@ -68,7 +70,7 @@ licenses:
68
70
  - MIT
69
71
  metadata:
70
72
  rubygems_mfa_required: 'true'
71
- post_install_message:
73
+ post_install_message:
72
74
  rdoc_options: []
73
75
  require_paths:
74
76
  - lib
@@ -83,8 +85,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
85
  - !ruby/object:Gem::Version
84
86
  version: '0'
85
87
  requirements: []
86
- rubygems_version: 3.5.11
87
- signing_key:
88
+ rubygems_version: 3.5.18
89
+ signing_key:
88
90
  specification_version: 4
89
91
  summary: Analytic
90
92
  test_files: []