loyalty_ref 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +27 -0
- data/LICENSE.txt +21 -0
- data/README.md +194 -0
- data/app/assets/javascripts/loyalty_ref.js +138 -0
- data/app/controllers/loyalty_ref/dashboard_controller.rb +91 -0
- data/app/controllers/loyalty_ref/tracking_controller.rb +30 -0
- data/app/helpers/loyalty_ref/application_helper.rb +56 -0
- data/app/helpers/loyalty_ref/dashboard_helper.rb +41 -0
- data/app/views/layouts/loyalty_ref/dashboard.html.erb +233 -0
- data/app/views/loyalty_ref/dashboard/analytics.html.erb +80 -0
- data/app/views/loyalty_ref/dashboard/index.html.erb +78 -0
- data/app/views/loyalty_ref/dashboard/referrals.html.erb +41 -0
- data/app/views/loyalty_ref/dashboard/transactions.html.erb +31 -0
- data/app/views/loyalty_ref/dashboard/user.html.erb +50 -0
- data/app/views/loyalty_ref/dashboard/users.html.erb +40 -0
- data/config/routes.rb +14 -0
- data/db/migrate/001_create_loyalty_ref_transactions.rb +21 -0
- data/db/migrate/002_create_loyalty_ref_referral_logs.rb +28 -0
- data/lib/generators/loyalty_ref/install/install_generator.rb +42 -0
- data/lib/generators/loyalty_ref/install/templates/initializer.rb +63 -0
- data/lib/generators/loyalty_ref/install/templates/user_model.rb +45 -0
- data/lib/loyalty_ref/configuration.rb +32 -0
- data/lib/loyalty_ref/engine.rb +34 -0
- data/lib/loyalty_ref/models/referral_log.rb +109 -0
- data/lib/loyalty_ref/models/transaction.rb +37 -0
- data/lib/loyalty_ref/version.rb +5 -0
- data/lib/loyalty_ref.rb +191 -0
- metadata +240 -0
@@ -0,0 +1,233 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>LoyaltyRef Dashboard</title>
|
7
|
+
<style>
|
8
|
+
* {
|
9
|
+
margin: 0;
|
10
|
+
padding: 0;
|
11
|
+
box-sizing: border-box;
|
12
|
+
}
|
13
|
+
|
14
|
+
body {
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
16
|
+
background: #f8fafc;
|
17
|
+
color: #1e293b;
|
18
|
+
line-height: 1.6;
|
19
|
+
}
|
20
|
+
|
21
|
+
.navbar {
|
22
|
+
background: #1e293b;
|
23
|
+
color: white;
|
24
|
+
padding: 1rem 2rem;
|
25
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
26
|
+
}
|
27
|
+
|
28
|
+
.navbar h1 {
|
29
|
+
font-size: 1.5rem;
|
30
|
+
font-weight: 600;
|
31
|
+
}
|
32
|
+
|
33
|
+
.container {
|
34
|
+
max-width: 1200px;
|
35
|
+
margin: 0 auto;
|
36
|
+
padding: 2rem;
|
37
|
+
}
|
38
|
+
|
39
|
+
.sidebar {
|
40
|
+
background: white;
|
41
|
+
border-radius: 8px;
|
42
|
+
padding: 1.5rem;
|
43
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
44
|
+
margin-bottom: 2rem;
|
45
|
+
}
|
46
|
+
|
47
|
+
.nav-links {
|
48
|
+
display: flex;
|
49
|
+
gap: 1rem;
|
50
|
+
flex-wrap: wrap;
|
51
|
+
}
|
52
|
+
|
53
|
+
.nav-links a {
|
54
|
+
color: #64748b;
|
55
|
+
text-decoration: none;
|
56
|
+
padding: 0.5rem 1rem;
|
57
|
+
border-radius: 6px;
|
58
|
+
transition: all 0.2s;
|
59
|
+
}
|
60
|
+
|
61
|
+
.nav-links a:hover,
|
62
|
+
.nav-links a.active {
|
63
|
+
background: #f1f5f9;
|
64
|
+
color: #1e293b;
|
65
|
+
}
|
66
|
+
|
67
|
+
.card {
|
68
|
+
background: white;
|
69
|
+
border-radius: 8px;
|
70
|
+
padding: 1.5rem;
|
71
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
72
|
+
margin-bottom: 1.5rem;
|
73
|
+
}
|
74
|
+
|
75
|
+
.card h2 {
|
76
|
+
font-size: 1.25rem;
|
77
|
+
font-weight: 600;
|
78
|
+
margin-bottom: 1rem;
|
79
|
+
color: #1e293b;
|
80
|
+
}
|
81
|
+
|
82
|
+
.stats-grid {
|
83
|
+
display: grid;
|
84
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
85
|
+
gap: 1rem;
|
86
|
+
margin-bottom: 2rem;
|
87
|
+
}
|
88
|
+
|
89
|
+
.stat-card {
|
90
|
+
background: white;
|
91
|
+
padding: 1.5rem;
|
92
|
+
border-radius: 8px;
|
93
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
94
|
+
text-align: center;
|
95
|
+
}
|
96
|
+
|
97
|
+
.stat-number {
|
98
|
+
font-size: 2rem;
|
99
|
+
font-weight: 700;
|
100
|
+
color: #3b82f6;
|
101
|
+
margin-bottom: 0.5rem;
|
102
|
+
}
|
103
|
+
|
104
|
+
.stat-label {
|
105
|
+
color: #64748b;
|
106
|
+
font-size: 0.875rem;
|
107
|
+
text-transform: uppercase;
|
108
|
+
letter-spacing: 0.05em;
|
109
|
+
}
|
110
|
+
|
111
|
+
.table {
|
112
|
+
width: 100%;
|
113
|
+
border-collapse: collapse;
|
114
|
+
margin-top: 1rem;
|
115
|
+
}
|
116
|
+
|
117
|
+
.table th,
|
118
|
+
.table td {
|
119
|
+
padding: 0.75rem;
|
120
|
+
text-align: left;
|
121
|
+
border-bottom: 1px solid #e2e8f0;
|
122
|
+
}
|
123
|
+
|
124
|
+
.table th {
|
125
|
+
background: #f8fafc;
|
126
|
+
font-weight: 600;
|
127
|
+
color: #475569;
|
128
|
+
}
|
129
|
+
|
130
|
+
.table tr:hover {
|
131
|
+
background: #f8fafc;
|
132
|
+
}
|
133
|
+
|
134
|
+
.badge {
|
135
|
+
display: inline-block;
|
136
|
+
padding: 0.25rem 0.5rem;
|
137
|
+
border-radius: 4px;
|
138
|
+
font-size: 0.75rem;
|
139
|
+
font-weight: 500;
|
140
|
+
text-transform: uppercase;
|
141
|
+
}
|
142
|
+
|
143
|
+
.badge.earn {
|
144
|
+
background: #dcfce7;
|
145
|
+
color: #166534;
|
146
|
+
}
|
147
|
+
|
148
|
+
.badge.redeem {
|
149
|
+
background: #fef2f2;
|
150
|
+
color: #dc2626;
|
151
|
+
}
|
152
|
+
|
153
|
+
.badge.silver {
|
154
|
+
background: #f1f5f9;
|
155
|
+
color: #475569;
|
156
|
+
}
|
157
|
+
|
158
|
+
.badge.gold {
|
159
|
+
background: #fef3c7;
|
160
|
+
color: #92400e;
|
161
|
+
}
|
162
|
+
|
163
|
+
.badge.platinum {
|
164
|
+
background: #e0e7ff;
|
165
|
+
color: #3730a3;
|
166
|
+
}
|
167
|
+
|
168
|
+
.btn {
|
169
|
+
display: inline-block;
|
170
|
+
padding: 0.5rem 1rem;
|
171
|
+
border-radius: 6px;
|
172
|
+
text-decoration: none;
|
173
|
+
font-weight: 500;
|
174
|
+
transition: all 0.2s;
|
175
|
+
border: none;
|
176
|
+
cursor: pointer;
|
177
|
+
}
|
178
|
+
|
179
|
+
.btn-primary {
|
180
|
+
background: #3b82f6;
|
181
|
+
color: white;
|
182
|
+
}
|
183
|
+
|
184
|
+
.btn-primary:hover {
|
185
|
+
background: #2563eb;
|
186
|
+
}
|
187
|
+
|
188
|
+
.pagination {
|
189
|
+
display: flex;
|
190
|
+
justify-content: center;
|
191
|
+
gap: 0.5rem;
|
192
|
+
margin-top: 2rem;
|
193
|
+
}
|
194
|
+
|
195
|
+
.pagination a {
|
196
|
+
padding: 0.5rem 0.75rem;
|
197
|
+
border: 1px solid #e2e8f0;
|
198
|
+
border-radius: 4px;
|
199
|
+
text-decoration: none;
|
200
|
+
color: #64748b;
|
201
|
+
}
|
202
|
+
|
203
|
+
.pagination a:hover {
|
204
|
+
background: #f1f5f9;
|
205
|
+
}
|
206
|
+
|
207
|
+
.pagination .current {
|
208
|
+
background: #3b82f6;
|
209
|
+
color: white;
|
210
|
+
border-color: #3b82f6;
|
211
|
+
}
|
212
|
+
</style>
|
213
|
+
</head>
|
214
|
+
<body>
|
215
|
+
<nav class="navbar">
|
216
|
+
<h1>💎 LoyaltyRef Dashboard</h1>
|
217
|
+
</nav>
|
218
|
+
|
219
|
+
<div class="container">
|
220
|
+
<div class="sidebar">
|
221
|
+
<nav class="nav-links">
|
222
|
+
<%= link_to "Dashboard", loyalty_ref.root_path, class: current_page?(loyalty_ref.root_path) ? "active" : "" %>
|
223
|
+
<%= link_to "Users", loyalty_ref.users_path, class: current_page?(loyalty_ref.users_path) ? "active" : "" %>
|
224
|
+
<%= link_to "Transactions", loyalty_ref.transactions_path, class: current_page?(loyalty_ref.transactions_path) ? "active" : "" %>
|
225
|
+
<%= link_to "Referrals", loyalty_ref.referrals_path, class: current_page?(loyalty_ref.referrals_path) ? "active" : "" %>
|
226
|
+
<%= link_to "Analytics", loyalty_ref.analytics_path, class: current_page?(loyalty_ref.analytics_path) ? "active" : "" %>
|
227
|
+
</nav>
|
228
|
+
</div>
|
229
|
+
|
230
|
+
<%= yield %>
|
231
|
+
</div>
|
232
|
+
</body>
|
233
|
+
</html>
|
@@ -0,0 +1,80 @@
|
|
1
|
+
<div class="stats-grid">
|
2
|
+
<div class="stat-card">
|
3
|
+
<div class="stat-number"><%= @total_clicks %></div>
|
4
|
+
<div class="stat-label">Total Clicks</div>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="stat-card">
|
8
|
+
<div class="stat-number"><%= @total_conversions %></div>
|
9
|
+
<div class="stat-label">Total Conversions</div>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="stat-card">
|
13
|
+
<div class="stat-number"><%= number_to_percentage(@conversion_rate, precision: 1) %></div>
|
14
|
+
<div class="stat-label">Conversion Rate</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="card">
|
19
|
+
<h2>Referrals by Device Type</h2>
|
20
|
+
<table class="table">
|
21
|
+
<thead>
|
22
|
+
<tr>
|
23
|
+
<th>Device Type</th>
|
24
|
+
<th>Count</th>
|
25
|
+
<th>Percentage</th>
|
26
|
+
</tr>
|
27
|
+
</thead>
|
28
|
+
<tbody>
|
29
|
+
<% @referrals_by_device.each do |device, count| %>
|
30
|
+
<tr>
|
31
|
+
<td><%= device.titleize %></td>
|
32
|
+
<td><%= count %></td>
|
33
|
+
<td><%= number_to_percentage((count.to_f / @total_clicks * 100), precision: 1) %></td>
|
34
|
+
</tr>
|
35
|
+
<% end %>
|
36
|
+
</tbody>
|
37
|
+
</table>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<div class="card">
|
41
|
+
<h2>Referrals by Browser</h2>
|
42
|
+
<table class="table">
|
43
|
+
<thead>
|
44
|
+
<tr>
|
45
|
+
<th>Browser</th>
|
46
|
+
<th>Count</th>
|
47
|
+
<th>Percentage</th>
|
48
|
+
</tr>
|
49
|
+
</thead>
|
50
|
+
<tbody>
|
51
|
+
<% @referrals_by_browser.each do |browser, count| %>
|
52
|
+
<tr>
|
53
|
+
<td><%= browser %></td>
|
54
|
+
<td><%= count %></td>
|
55
|
+
<td><%= number_to_percentage((count.to_f / @total_clicks * 100), precision: 1) %></td>
|
56
|
+
</tr>
|
57
|
+
<% end %>
|
58
|
+
</tbody>
|
59
|
+
</table>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div class="card">
|
63
|
+
<h2>Daily Referral Clicks (Last 30 Days)</h2>
|
64
|
+
<table class="table">
|
65
|
+
<thead>
|
66
|
+
<tr>
|
67
|
+
<th>Date</th>
|
68
|
+
<th>Clicks</th>
|
69
|
+
</tr>
|
70
|
+
</thead>
|
71
|
+
<tbody>
|
72
|
+
<% @daily_clicks.each do |date, count| %>
|
73
|
+
<tr>
|
74
|
+
<td><%= Date.parse(date).strftime("%b %d, %Y") %></td>
|
75
|
+
<td><%= count %></td>
|
76
|
+
</tr>
|
77
|
+
<% end %>
|
78
|
+
</tbody>
|
79
|
+
</table>
|
80
|
+
</div>
|
@@ -0,0 +1,78 @@
|
|
1
|
+
<div class="stats-grid">
|
2
|
+
<div class="stat-card">
|
3
|
+
<div class="stat-number"><%= @total_users %></div>
|
4
|
+
<div class="stat-label">Total Users</div>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="stat-card">
|
8
|
+
<div class="stat-number"><%= @total_transactions %></div>
|
9
|
+
<div class="stat-label">Total Transactions</div>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="stat-card">
|
13
|
+
<div class="stat-number"><%= @total_referrals %></div>
|
14
|
+
<div class="stat-label">Total Referrals</div>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="stat-card">
|
18
|
+
<div class="stat-number"><%= number_to_percentage(@conversion_rate, precision: 1) %></div>
|
19
|
+
<div class="stat-label">Conversion Rate</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="card">
|
24
|
+
<h2>Recent Transactions</h2>
|
25
|
+
<table class="table">
|
26
|
+
<thead>
|
27
|
+
<tr>
|
28
|
+
<th>User</th>
|
29
|
+
<th>Type</th>
|
30
|
+
<th>Points</th>
|
31
|
+
<th>Date</th>
|
32
|
+
</tr>
|
33
|
+
</thead>
|
34
|
+
<tbody>
|
35
|
+
<% @recent_transactions.each do |transaction| %>
|
36
|
+
<tr>
|
37
|
+
<td><%= transaction.user.email rescue transaction.user.id %></td>
|
38
|
+
<td>
|
39
|
+
<span class="badge <%= transaction.transaction_type %>">
|
40
|
+
<%= transaction.transaction_type.titleize %>
|
41
|
+
</span>
|
42
|
+
</td>
|
43
|
+
<td><%= transaction.points %></td>
|
44
|
+
<td><%= transaction.created_at.strftime("%b %d, %Y") %></td>
|
45
|
+
</tr>
|
46
|
+
<% end %>
|
47
|
+
</tbody>
|
48
|
+
</table>
|
49
|
+
</div>
|
50
|
+
|
51
|
+
<div class="card">
|
52
|
+
<h2>Top Users by Points</h2>
|
53
|
+
<table class="table">
|
54
|
+
<thead>
|
55
|
+
<tr>
|
56
|
+
<th>User</th>
|
57
|
+
<th>Total Points</th>
|
58
|
+
<th>Tier</th>
|
59
|
+
</tr>
|
60
|
+
</thead>
|
61
|
+
<tbody>
|
62
|
+
<% @top_users.each do |user| %>
|
63
|
+
<tr>
|
64
|
+
<td><%= user.email rescue user.id %></td>
|
65
|
+
<td><%= LoyaltyRef.balance(user) %></td>
|
66
|
+
<td>
|
67
|
+
<% tier = LoyaltyRef.tier(user) %>
|
68
|
+
<% if tier %>
|
69
|
+
<span class="badge <%= tier.downcase %>"><%= tier %></span>
|
70
|
+
<% else %>
|
71
|
+
<span class="badge">None</span>
|
72
|
+
<% end %>
|
73
|
+
</td>
|
74
|
+
</tr>
|
75
|
+
<% end %>
|
76
|
+
</tbody>
|
77
|
+
</table>
|
78
|
+
</div>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<h2>Referrals</h2>
|
3
|
+
<table class="table">
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th>Referral Code</th>
|
7
|
+
<th>Clicked At</th>
|
8
|
+
<th>User Agent</th>
|
9
|
+
<th>IP Address</th>
|
10
|
+
<th>Signed Up At</th>
|
11
|
+
<th>Referee</th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody>
|
15
|
+
<% @referrals.each do |referral| %>
|
16
|
+
<tr>
|
17
|
+
<td><code><%= referral.referral_code %></code></td>
|
18
|
+
<td><%= referral.clicked_at.strftime("%b %d, %Y %H:%M") %></td>
|
19
|
+
<td><%= truncate(referral.user_agent, length: 50) %></td>
|
20
|
+
<td><%= referral.ip_address %></td>
|
21
|
+
<td>
|
22
|
+
<% if referral.signed_up_at %>
|
23
|
+
<%= referral.signed_up_at.strftime("%b %d, %Y %H:%M") %>
|
24
|
+
<% else %>
|
25
|
+
<span class="text-muted">Not signed up</span>
|
26
|
+
<% end %>
|
27
|
+
</td>
|
28
|
+
<td>
|
29
|
+
<% if referral.referee %>
|
30
|
+
<%= referral.referee.email rescue referral.referee.id %>
|
31
|
+
<% else %>
|
32
|
+
<span class="text-muted">-</span>
|
33
|
+
<% end %>
|
34
|
+
</td>
|
35
|
+
</tr>
|
36
|
+
<% end %>
|
37
|
+
</tbody>
|
38
|
+
</table>
|
39
|
+
|
40
|
+
<%= loyalty_paginate @referrals %>
|
41
|
+
</div>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<h2>Transactions</h2>
|
3
|
+
<table class="table">
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th>User</th>
|
7
|
+
<th>Type</th>
|
8
|
+
<th>Points</th>
|
9
|
+
<th>Date</th>
|
10
|
+
<th>Description</th>
|
11
|
+
</tr>
|
12
|
+
</thead>
|
13
|
+
<tbody>
|
14
|
+
<% @transactions.each do |transaction| %>
|
15
|
+
<tr>
|
16
|
+
<td><%= transaction.user.email rescue transaction.user.id %></td>
|
17
|
+
<td>
|
18
|
+
<span class="badge <%= transaction.transaction_type %>">
|
19
|
+
<%= transaction.transaction_type.titleize %>
|
20
|
+
</span>
|
21
|
+
</td>
|
22
|
+
<td><%= transaction.points %></td>
|
23
|
+
<td><%= transaction.created_at.strftime("%b %d, %Y") %></td>
|
24
|
+
<td><%= transaction.description %></td>
|
25
|
+
</tr>
|
26
|
+
<% end %>
|
27
|
+
</tbody>
|
28
|
+
</table>
|
29
|
+
|
30
|
+
<%= loyalty_paginate @transactions %>
|
31
|
+
</div>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<h2>User Details: <%= @user.email %></h2>
|
3
|
+
|
4
|
+
<div class="stats">
|
5
|
+
<div class="stat">
|
6
|
+
<h3>Points Balance</h3>
|
7
|
+
<p class="big-number"><%= @balance %></p>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="stat">
|
11
|
+
<h3>Current Tier</h3>
|
12
|
+
<p class="big-number"><%= @tier || 'None' %></p>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="stat">
|
16
|
+
<h3>Referral Code</h3>
|
17
|
+
<p><code><%= @user.referral_code %></code></p>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<h3>Recent Transactions</h3>
|
22
|
+
<table class="table">
|
23
|
+
<thead>
|
24
|
+
<tr>
|
25
|
+
<th>Type</th>
|
26
|
+
<th>Points</th>
|
27
|
+
<th>Date</th>
|
28
|
+
<th>Description</th>
|
29
|
+
</tr>
|
30
|
+
</thead>
|
31
|
+
<tbody>
|
32
|
+
<% @transactions.each do |transaction| %>
|
33
|
+
<tr>
|
34
|
+
<td>
|
35
|
+
<span class="badge <%= transaction.transaction_type %>">
|
36
|
+
<%= transaction.transaction_type.titleize %>
|
37
|
+
</span>
|
38
|
+
</td>
|
39
|
+
<td><%= transaction.points %></td>
|
40
|
+
<td><%= transaction.created_at.strftime("%b %d, %Y") %></td>
|
41
|
+
<td><%= transaction.description %></td>
|
42
|
+
</tr>
|
43
|
+
<% end %>
|
44
|
+
</tbody>
|
45
|
+
</table>
|
46
|
+
|
47
|
+
<%= loyalty_paginate @transactions %>
|
48
|
+
|
49
|
+
<%= link_to 'Back to Users', loyalty_ref.users_path, class: 'btn btn-secondary' %>
|
50
|
+
</div>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<h2>Users</h2>
|
3
|
+
<table class="table">
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th>User</th>
|
7
|
+
<th>Points Balance</th>
|
8
|
+
<th>Current Tier</th>
|
9
|
+
<th>Referral Code</th>
|
10
|
+
<th>Total Referrals</th>
|
11
|
+
<th>Joined</th>
|
12
|
+
<th>Actions</th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody>
|
16
|
+
<% @users.each do |user| %>
|
17
|
+
<tr>
|
18
|
+
<td><%= user.email rescue user.id %></td>
|
19
|
+
<td><strong><%= LoyaltyRef.balance(user) %></strong></td>
|
20
|
+
<td>
|
21
|
+
<% tier = LoyaltyRef.tier(user) %>
|
22
|
+
<% if tier %>
|
23
|
+
<span class="badge <%= tier.downcase %>"><%= tier %></span>
|
24
|
+
<% else %>
|
25
|
+
<span class="badge">None</span>
|
26
|
+
<% end %>
|
27
|
+
</td>
|
28
|
+
<td><code><%= user.referral_code %></code></td>
|
29
|
+
<td><%= user.total_referrals rescue 0 %></td>
|
30
|
+
<td><%= user.created_at.strftime("%b %d, %Y") %></td>
|
31
|
+
<td>
|
32
|
+
<%= link_to "View Details", loyalty_ref.user_path(user), class: "btn btn-primary" %>
|
33
|
+
</td>
|
34
|
+
</tr>
|
35
|
+
<% end %>
|
36
|
+
</tbody>
|
37
|
+
</table>
|
38
|
+
|
39
|
+
<%= loyalty_paginate @users %>
|
40
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
LoyaltyRef::Engine.routes.draw do
|
4
|
+
root to: "dashboard#index"
|
5
|
+
|
6
|
+
get "users", to: "dashboard#users"
|
7
|
+
get "users/:id", to: "dashboard#user", as: :user
|
8
|
+
get "transactions", to: "dashboard#transactions"
|
9
|
+
get "referrals", to: "dashboard#referrals"
|
10
|
+
get "analytics", to: "dashboard#analytics"
|
11
|
+
|
12
|
+
# Tracking endpoint for device data collection
|
13
|
+
post "track_referral", to: "tracking#track_referral", as: :track_referral
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateLoyaltyRefTransactions < ActiveRecord::Migration[7.0]
|
4
|
+
def change
|
5
|
+
create_table :loyalty_ref_transactions do |t|
|
6
|
+
t.references :user, polymorphic: true, null: false, index: true
|
7
|
+
t.integer :points, null: false
|
8
|
+
t.string :transaction_type, null: false
|
9
|
+
t.json :event_data
|
10
|
+
t.json :offer_data
|
11
|
+
t.datetime :expires_at
|
12
|
+
t.text :description
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
add_index :loyalty_ref_transactions, :transaction_type
|
18
|
+
add_index :loyalty_ref_transactions, :expires_at
|
19
|
+
add_index :loyalty_ref_transactions, :created_at
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateLoyaltyRefReferralLogs < ActiveRecord::Migration[7.0]
|
4
|
+
def change
|
5
|
+
create_table :loyalty_ref_referral_logs do |t|
|
6
|
+
t.string :referral_code, null: false
|
7
|
+
t.references :referee, polymorphic: true, null: true
|
8
|
+
t.string :user_agent
|
9
|
+
t.string :ip_address
|
10
|
+
t.string :referrer
|
11
|
+
t.datetime :clicked_at, null: false
|
12
|
+
t.datetime :signed_up_at
|
13
|
+
t.string :device_type
|
14
|
+
t.string :browser
|
15
|
+
t.json :geo_data
|
16
|
+
t.json :device_data
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
|
21
|
+
add_index :loyalty_ref_referral_logs, :referral_code
|
22
|
+
add_index :loyalty_ref_referral_logs, :clicked_at
|
23
|
+
add_index :loyalty_ref_referral_logs, :signed_up_at
|
24
|
+
add_index :loyalty_ref_referral_logs, :ip_address
|
25
|
+
add_index :loyalty_ref_referral_logs, :device_type
|
26
|
+
add_index :loyalty_ref_referral_logs, :browser
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
|
5
|
+
module LoyaltyRef
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
def create_initializer
|
11
|
+
template "initializer.rb", "config/initializers/loyalty_ref.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_user_model_migration
|
15
|
+
generate_migration = ask("Would you like to add referral_code and tier columns to your User model? (y/n)")
|
16
|
+
|
17
|
+
if generate_migration.downcase == "y"
|
18
|
+
generate "migration", "AddLoyaltyRefToUsers referral_code:string tier:string"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_user_model_methods
|
23
|
+
user_class = ask("What is your user model class name? (default: User)")
|
24
|
+
user_class = "User" if user_class.blank?
|
25
|
+
|
26
|
+
template "user_model.rb", "app/models/#{user_class.downcase}.rb", force: true
|
27
|
+
end
|
28
|
+
|
29
|
+
def show_instructions
|
30
|
+
puts "\n" + "="*60
|
31
|
+
puts "🎉 LoyaltyRef has been installed successfully!"
|
32
|
+
puts "="*60
|
33
|
+
puts "\nNext steps:"
|
34
|
+
puts "1. Run: rails db:migrate"
|
35
|
+
puts "2. Customize config/initializers/loyalty_ref.rb"
|
36
|
+
puts "3. Access admin dashboard at: /loyalty"
|
37
|
+
puts "\nFor more information, visit: https://github.com/rails_to_rescue/loyalty_ref"
|
38
|
+
puts "="*60
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|