mirror of
https://github.com/usetrmnl/plugins.git
synced 2026-08-13 14:26:58 -07:00
Shopify
This commit is contained in:
@@ -17,6 +17,7 @@ community Recipes:
|
||||
- [Hacker News](/lib/hacker_news)
|
||||
- [Lunar Calendar](/lib/lunar_calendar)
|
||||
- [Lunch Money](/lib/lunch_money)
|
||||
- [Shopify](/lib/shopify)
|
||||
- [Stock Price](/lib/stock_price)
|
||||
- [Tempest Weather Station](/lib/tempest_weather_station)
|
||||
- [Todoist](/lib/todoist)
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
module Plugins
|
||||
class Shopify < Base
|
||||
|
||||
def locals
|
||||
{ metrics:, histogram:, currency:, comparison_period: comparison_period.map { it.to_date.to_s } }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metrics
|
||||
kpi = {}
|
||||
kpi[:open_order_count] = fetch_order_count('open')
|
||||
kpi[:closed_order_count] = fetch_order_count('closed')
|
||||
|
||||
total_orders = kpi[:open_order_count] + kpi[:closed_order_count]
|
||||
|
||||
if total_orders.positive?
|
||||
fetch_orders
|
||||
kpi[:order_sales] = calculate_sale_value || 0
|
||||
kpi[:aov] = (kpi[:order_sales] / (kpi[:open_order_count] + kpi[:closed_order_count])).round(2)
|
||||
end
|
||||
|
||||
kpi
|
||||
rescue InvalidURL
|
||||
{ errors: "Invalid Store Web Address or Access Token" }
|
||||
end
|
||||
|
||||
def fetch_orders
|
||||
@orders ||= query_sales_orders(lookback_period.to_s, nil)
|
||||
@comparison_orders ||= query_sales_orders(*comparison_period)
|
||||
end
|
||||
|
||||
def histogram
|
||||
return { current: [], comparison: [] } if @orders.nil?
|
||||
|
||||
current = prepare_charting_data(@orders)
|
||||
comparison = prepare_charting_data(@comparison_orders)
|
||||
comparison = normalize_with_current(comparison, current)
|
||||
|
||||
{ current:, comparison: }
|
||||
end
|
||||
|
||||
def prepare_charting_data(orders)
|
||||
return [] if orders.nil? || orders.empty?
|
||||
|
||||
order_data = orders&.map { { date: Date.parse(it['created_at']).to_s, value: it['current_subtotal_price'].to_f } }
|
||||
&.group_by { it[:date] }
|
||||
&.map { |k, v| { date: k, value: v.sum { it[:value] }.round(2) } }
|
||||
|
||||
dates = order_data.map { it[:date] }
|
||||
|
||||
min_date = Date.parse(dates.min)
|
||||
max_date = Date.parse(dates.max)
|
||||
|
||||
all_dates_hash = (min_date..max_date).each_with_object({}) do |date, hash|
|
||||
hash[date.to_s] = { date: date.to_s, value: 0.0 }
|
||||
end
|
||||
|
||||
order_data.each do |order|
|
||||
all_dates_hash[order[:date]] = order
|
||||
end
|
||||
|
||||
all_dates_hash.values.sort_by { it[:date] }
|
||||
end
|
||||
|
||||
def normalize_with_current(comparison, current)
|
||||
normalized_data = []
|
||||
case settings['lookback_period']
|
||||
when 'last_7_days'
|
||||
current.each_with_index do |data, index|
|
||||
next if comparison[index].nil?
|
||||
|
||||
normalized_data.push({ date: data[:date], value: comparison[index][:value] })
|
||||
end
|
||||
else
|
||||
current.each do |data|
|
||||
selected_date = comparison.select { (Date.parse(it[:date]) + 1.month).to_s == data[:date] }.first
|
||||
next unless selected_date
|
||||
|
||||
normalized_data.push({ date: data[:date], value: selected_date[:value] })
|
||||
end
|
||||
end
|
||||
|
||||
normalized_data
|
||||
end
|
||||
|
||||
def calculate_sale_value
|
||||
@orders&.sum { |o| o['current_subtotal_price'].to_f }
|
||||
end
|
||||
|
||||
def lookback_period
|
||||
case settings['lookback_period']
|
||||
when 'last_7_days'
|
||||
(user.datetime_now - 7.days).iso8601
|
||||
when 'last_30_days'
|
||||
(user.datetime_now - 30.days).iso8601
|
||||
when 'month_to_date'
|
||||
user.datetime_now.beginning_of_month.iso8601
|
||||
end
|
||||
end
|
||||
|
||||
def comparison_period
|
||||
case settings['lookback_period']
|
||||
when 'last_7_days'
|
||||
[(user.datetime_now - 14.days).iso8601, (user.datetime_now - 7.days).iso8601]
|
||||
when 'last_30_days'
|
||||
[(user.datetime_now - 60.days).iso8601, (user.datetime_now - 30.days).iso8601]
|
||||
when 'month_to_date'
|
||||
[(user.datetime_now.beginning_of_month - 1.month).iso8601, (user.datetime_now - 1.month).iso8601]
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_order_count(status = 'any')
|
||||
client.get(
|
||||
path: 'orders/count',
|
||||
query: {
|
||||
status: status,
|
||||
created_at_min: ">=#{lookback_period}"
|
||||
}
|
||||
).body['count']
|
||||
rescue ShopifyAPI::Errors::HttpResponseError => e
|
||||
error_message = e.response.body['errors']
|
||||
raise InvalidURL if error_message.include?('Not Found') || error_message.include?('Invalid API key or access token')
|
||||
end
|
||||
|
||||
def query_sales_orders(start_window, end_window)
|
||||
orders = []
|
||||
run_loop = true
|
||||
|
||||
while run_loop
|
||||
retrieved_orders = client.get(
|
||||
path: 'orders',
|
||||
query: {
|
||||
status: 'any',
|
||||
limit: 250,
|
||||
created_at_min: start_window,
|
||||
processed_at_max: orders&.last&.dig('created_at') || end_window
|
||||
}
|
||||
).body['orders']
|
||||
run_loop = false if retrieved_orders.count < 250
|
||||
orders += retrieved_orders.reject { |m| m['cancelled_at'].present? }
|
||||
end
|
||||
|
||||
orders
|
||||
end
|
||||
|
||||
def currency
|
||||
code = client.get(path: 'shop').body['shop']['currency']
|
||||
|
||||
{
|
||||
'USD' => '$',
|
||||
'EUR' => '€',
|
||||
'CAD' => '$',
|
||||
'CHF' => '₣',
|
||||
'GBP' => '£',
|
||||
'KRW' => '₩',
|
||||
'CNY' => '¥',
|
||||
'JPY' => '¥',
|
||||
'INR' => '₹',
|
||||
'ZAR' => 'R'
|
||||
}[code] || '$'
|
||||
rescue ShopifyAPI::Errors::HttpResponseError => e
|
||||
error_message = e.response.body['errors']
|
||||
raise InvalidURL if error_message.include?('Not Found') || error_message.include?('Invalid API key or access token')
|
||||
end
|
||||
|
||||
def client = ShopifyAPI::Clients::Rest::Admin.new(session:)
|
||||
|
||||
def session = ShopifyAPI::Auth::Session.new(shop:, access_token:)
|
||||
|
||||
def myshopify_domain
|
||||
shop = settings['shop']
|
||||
shop.include?('myshopify') ? shop.split('.')[0] : shop
|
||||
end
|
||||
|
||||
def shop = "#{myshopify_domain.strip}.myshopify.com"
|
||||
|
||||
def access_token = settings['access_token']
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,184 @@
|
||||
<% unless (metrics[:errors].present?) %>
|
||||
<div class="view view--full">
|
||||
<div class="layout layout--col gap--space-between">
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
<div class="grid">
|
||||
<div class="item col--span-2">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--large value--tnums" data-value-type="number"><%= number_to_currency(metrics[:order_sales], unit: currency, precision: 0) %></span>
|
||||
<span class="label">Total Sales</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item col--span-1">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--small value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:open_order_count]) %></span>
|
||||
<span class="label">Pending Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item col--span-1">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xsmall value--tnums" data-value-type="date">
|
||||
<div class="w--14 h--1.5 mb--2 bg--black" style="border-radius: 20px;"></div>
|
||||
<% if histogram[:current].present? %>
|
||||
<%= DateTime.parse(histogram[:current].first[:date]).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(histogram[:current].last[:date]).strftime("%b %d") %>
|
||||
<% end %>
|
||||
</span>
|
||||
<span class="label"><%= histogram.first[0].capitalize %></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border--h-5 w--full"></div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="row">
|
||||
<div class="grid">
|
||||
<div class="item col--span-2">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--tnums" data-value-type="number"><%= number_to_currency(metrics[:aov], unit: currency, precision: 0) %></span>
|
||||
<span class="label">AOV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item col--span-1">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--small value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:closed_order_count]) %></span>
|
||||
<span class="label">Fulfilled Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item col--span-1">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xsmall value--tnums" data-value-type="date">
|
||||
<div class="w--14 h--1.5 mb--2 bg--gray-5"></div>
|
||||
<%= DateTime.parse(comparison_period.first).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(comparison_period.last).strftime("%b %d") %>
|
||||
</span>
|
||||
<span class="label"><%= histogram.to_a.last[0].capitalize %></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render 'plugins/chart_js', libs: [:highcharts, :highcharts_more, :highcharts_pattern_fill] %>
|
||||
|
||||
<% formatted_data = histogram.map { |key, period| { name: key.capitalize, data: period.map { [it[:date], it[:value]] }}} %>
|
||||
<% chart_id = "chart-#{SecureRandom.hex(6)}" %>
|
||||
<div id="<%= chart_id %>" class="w--full"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
var data = <%= raw formatted_data.to_json %>;
|
||||
|
||||
Highcharts.chart('<%= chart_id %>', {
|
||||
chart: {
|
||||
type: 'spline',
|
||||
height: 203,
|
||||
width: null,
|
||||
animation: false,
|
||||
spacing: [10, 10, 5, 10]
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
plotOptions: {
|
||||
series: {
|
||||
animation: false,
|
||||
enableMouseTracking: false,
|
||||
states: {
|
||||
hover: { enabled: false }
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: data[0].data,
|
||||
lineWidth: 4,
|
||||
color: '#000000',
|
||||
name: data[0].name,
|
||||
zIndex: 2
|
||||
}, {
|
||||
data: data[1].data,
|
||||
lineWidth: 5,
|
||||
name: data[1].name,
|
||||
zIndex: 1,
|
||||
color: {
|
||||
pattern: {
|
||||
image: 'https://usetrmnl.com/images/grayscale/gray-5.png',
|
||||
width: 12,
|
||||
height: 12
|
||||
}
|
||||
}
|
||||
}],
|
||||
tooltip: { enabled: false },
|
||||
legend: { enabled: false },
|
||||
yAxis: {
|
||||
labels: {
|
||||
style: { fontSize: '16px', color: '#000000' }
|
||||
},
|
||||
gridLineDashStyle: 'shortdot',
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: '#000000',
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: null
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
style: { fontSize: '16px', color: '#000000' },
|
||||
padding: 5,
|
||||
y: 25
|
||||
},
|
||||
lineWidth: 0,
|
||||
gridLineDashStyle: 'dot',
|
||||
tickWidth: 1,
|
||||
tickLength: 0,
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: '#000000',
|
||||
tickPixelInterval: 120,
|
||||
offset: 0,
|
||||
margin: 0,
|
||||
title: {
|
||||
text: null
|
||||
}
|
||||
},
|
||||
credits: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="title_bar">
|
||||
<img class="image" src="<%= Rails.application.credentials.base_url %>/images/plugins/shopify--render.svg" />
|
||||
<span class="title">Shopify</span>
|
||||
<span class="instance"><%= instance_name %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render partial: 'plugins/errors/full', locals: {
|
||||
icon: 'shopify--render.svg',
|
||||
title: 'Shopify',
|
||||
instance: instance_name,
|
||||
error_message: metrics[:errors]
|
||||
}
|
||||
%>
|
||||
<% end %>
|
||||
@@ -0,0 +1,178 @@
|
||||
<% unless (metrics[:errors].present?) %>
|
||||
<div class="view view--half_horizontal">
|
||||
<div class="layout layout--col gap--space-between">
|
||||
<div class="grid h-full">
|
||||
<div class="col--span-3 col gap--space-between">
|
||||
<div class="grid grid--cols-2 gap--[9px]" data-adjust-grid-gaps="false">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--tnums" data-value-type="number"><%= number_to_currency(metrics[:order_sales], unit: currency, precision: 0) %></span>
|
||||
<span class="label">Total Sales</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--tnums" data-value-type="number"><%= number_to_currency(metrics[:aov], unit: currency, precision: 0) %></span>
|
||||
<span class="label">AOV</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full b-h-gray-5"></div>
|
||||
<div class="grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xsmall value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:open_order_count]) %></span>
|
||||
<span class="label">Pending Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xsmall value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:closed_order_count]) %></span>
|
||||
<span class="label">Fulfilled Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full b-h-gray-5"></div>
|
||||
<div class="grid grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xxsmall value--tnums" data-value-type="date">
|
||||
<div class="w--12 h--1.5 mb--2 bg--gray-5"></div>
|
||||
<%= DateTime.parse(comparison_period.first).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(comparison_period.last).strftime("%b %d") %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xxsmall value--tnums" data-value-type="date">
|
||||
<div class="w--12 h--1.5 mb--2 bg--black" style="border-radius: 20px;"></div>
|
||||
<% if histogram[:current].present? %>
|
||||
<%= DateTime.parse(histogram[:current].first[:date]).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(histogram[:current].last[:date]).strftime("%b %d") %>
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col--span-3 col gap--space-between">
|
||||
<% formatted_data = histogram.map { |key, period| { name: key.capitalize, data: period.map { [it[:date], it[:value]] }}} %>
|
||||
<% chart_id = "chart-#{SecureRandom.hex(6)}" %>
|
||||
<div id="<%= chart_id %>" class="w--full"></div>
|
||||
|
||||
<%= render 'plugins/chart_js', libs: [:highcharts, :highcharts_more, :highcharts_pattern_fill] %>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
var data = <%= raw formatted_data.to_json %>;
|
||||
|
||||
Highcharts.chart('<%= chart_id %>', {
|
||||
chart: {
|
||||
type: 'spline',
|
||||
height: 180,
|
||||
width: 376,
|
||||
animation: false,
|
||||
spacing: [10, 0, 10, 0]
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
plotOptions: {
|
||||
series: {
|
||||
animation: false,
|
||||
enableMouseTracking: false,
|
||||
states: {
|
||||
hover: { enabled: false }
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: data[0].data,
|
||||
lineWidth: 4,
|
||||
color: '#000000',
|
||||
name: data[0].name,
|
||||
zIndex: 2
|
||||
}, {
|
||||
data: data[1].data,
|
||||
lineWidth: 5,
|
||||
name: data[1].name,
|
||||
zIndex: 1,
|
||||
color: {
|
||||
pattern: {
|
||||
image: 'https://usetrmnl.com/images/grayscale/gray-5.png',
|
||||
width: 12,
|
||||
height: 12
|
||||
}
|
||||
}
|
||||
}],
|
||||
tooltip: { enabled: false },
|
||||
legend: { enabled: false },
|
||||
yAxis: {
|
||||
labels: {
|
||||
style: { fontSize: '16px', color: '#000000' },
|
||||
formatter: function() {
|
||||
return '<%= currency %>' + (this.value / 1000) + 'k';
|
||||
}
|
||||
},
|
||||
gridLineDashStyle: 'shortdot',
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: '#000000',
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: null
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
style: { fontSize: '16px', color: '#000000' },
|
||||
padding: 5,
|
||||
y: 25
|
||||
},
|
||||
lineWidth: 0,
|
||||
gridLineDashStyle: 'dot',
|
||||
tickWidth: 1,
|
||||
tickLength: 0,
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: '#000000',
|
||||
tickPixelInterval: 120,
|
||||
offset: 0,
|
||||
margin: 0,
|
||||
title: {
|
||||
text: null
|
||||
}
|
||||
},
|
||||
credits: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="title_bar">
|
||||
<img class="image" src="<%= Rails.application.credentials.base_url %>/images/plugins/shopify--render.svg" />
|
||||
<span class="title">Shopify</span>
|
||||
<span class="instance"><%= instance_name %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render partial: 'plugins/errors/half_horizontal', locals: {
|
||||
icon: 'shopify--render.svg',
|
||||
title: 'Shopify',
|
||||
instance: instance_name,
|
||||
error_message: metrics[:errors]
|
||||
} %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,177 @@
|
||||
<% unless (metrics[:errors].present?) %>
|
||||
<div class="view view--half_vertical">
|
||||
<div class="layout layout--col gap--space-between">
|
||||
<div class="grid grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--tnums" data-value-type="number"><%= number_to_currency(metrics[:order_sales], unit: currency, precision: 0) %></span>
|
||||
<span class="label">Total Sales</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--tnums" data-value-type="number"><%= number_to_currency(metrics[:aov], unit: currency, precision: 0) %></span>
|
||||
<span class="label">AOV</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border--h-5 w--full"></div>
|
||||
|
||||
<div class="grid grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--small value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:open_order_count]) %></span>
|
||||
<span class="label">Pending Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--small value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:closed_order_count]) %></span>
|
||||
<span class="label">Fulfilled Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border--h-5 w--full"></div>
|
||||
|
||||
<div class="grid grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xxsmall value--tnums" data-value-type="date">
|
||||
<div class="w--12 h--1.5 mb--2 bg--gray-5"></div>
|
||||
<%= DateTime.parse(comparison_period.first).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(comparison_period.last).strftime("%b %d") %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xxsmall value--tnums" data-value-type="date">
|
||||
<div class="w--12 h--1.5 mb--2 bg--black" style="border-radius: 20px;"></div>
|
||||
<% if histogram[:current].present? %>
|
||||
<%= DateTime.parse(histogram[:current].first[:date]).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(histogram[:current].last[:date]).strftime("%b %d") %>
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% formatted_data = histogram.map { |key, period| { name: key.capitalize, data: period.map { [it[:date], it[:value]] }}} %>
|
||||
<% chart_id = "chart-#{SecureRandom.hex(6)}" %>
|
||||
<div id="<%= chart_id %>" class="w--full"></div>
|
||||
|
||||
<%= render 'plugins/chart_js', libs: [:highcharts, :highcharts_more, :highcharts_pattern_fill] %>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
var data = <%= raw formatted_data.to_json %>;
|
||||
|
||||
Highcharts.chart('<%= chart_id %>', {
|
||||
chart: {
|
||||
type: 'spline',
|
||||
height: 180,
|
||||
width: null,
|
||||
animation: false,
|
||||
spacing: [10, 0, 0, 0]
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
plotOptions: {
|
||||
series: {
|
||||
animation: false,
|
||||
enableMouseTracking: false,
|
||||
states: {
|
||||
hover: { enabled: false }
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: data[0].data,
|
||||
lineWidth: 4,
|
||||
color: '#000000',
|
||||
name: data[0].name,
|
||||
zIndex: 2
|
||||
}, {
|
||||
data: data[1].data,
|
||||
lineWidth: 5,
|
||||
name: data[1].name,
|
||||
zIndex: 1,
|
||||
color: {
|
||||
pattern: {
|
||||
image: 'https://usetrmnl.com/images/grayscale/gray-5.png',
|
||||
width: 12,
|
||||
height: 12
|
||||
}
|
||||
}
|
||||
}],
|
||||
tooltip: { enabled: false },
|
||||
legend: { enabled: false },
|
||||
yAxis: {
|
||||
labels: {
|
||||
style: { fontSize: '16px', color: '#000000' },
|
||||
formatter: function() {
|
||||
return '<%= currency %>' + (this.value / 1000) + 'k';
|
||||
}
|
||||
},
|
||||
gridLineDashStyle: 'shortdot',
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: '#000000',
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: null
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
style: { fontSize: '16px', color: '#000000' },
|
||||
padding: 5,
|
||||
y: 25
|
||||
},
|
||||
lineWidth: 0,
|
||||
gridLineDashStyle: 'dot',
|
||||
tickWidth: 1,
|
||||
tickLength: 0,
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: '#000000',
|
||||
tickPixelInterval: 120,
|
||||
offset: 0,
|
||||
margin: 0,
|
||||
title: {
|
||||
text: null
|
||||
}
|
||||
},
|
||||
credits: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div class="title_bar">
|
||||
<img class="image" src="<%= Rails.application.credentials.base_url %>/images/plugins/shopify--render.svg" />
|
||||
<span class="title">Shopify</span>
|
||||
<span class="instance"><%= instance_name %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render partial: 'plugins/errors/half_vertical', locals: {
|
||||
icon: 'shopify--render.svg',
|
||||
title: 'Shopify',
|
||||
instance: instance_name,
|
||||
error_message: metrics[:errors]
|
||||
} %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,134 @@
|
||||
<% unless (metrics[:errors].present?) %>
|
||||
<div class="view view--quadrant">
|
||||
<div class="layout layout--col gap--space-between">
|
||||
<div class="grid grid--cols-2 gap--[9px]" data-adjust-grid-gaps="false">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--tnums" data-value-type="number"><%= number_to_currency(metrics[:order_sales], unit: currency, precision: 0) %></span>
|
||||
<span class="label">Total Sales</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<% formatted_data = histogram.map { |key, period| { name: key.capitalize, data: period.map { [it[:date], it[:value]] }}} %>
|
||||
<% chart_id = "chart-#{SecureRandom.hex(6)}" %>
|
||||
|
||||
<div id="<%= chart_id %>" class="w--full"></div>
|
||||
|
||||
<%= render 'plugins/chart_js', libs: [:highcharts, :highcharts_more, :highcharts_pattern_fill] %>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
var data = <%= raw formatted_data.to_json %>;
|
||||
|
||||
Highcharts.chart('<%= chart_id %>', {
|
||||
chart: {
|
||||
type: 'spline',
|
||||
height: 60,
|
||||
width: null,
|
||||
animation: false,
|
||||
spacing: [0, 0, 0, 0]
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
plotOptions: {
|
||||
series: {
|
||||
animation: false,
|
||||
enableMouseTracking: false,
|
||||
states: {
|
||||
hover: { enabled: false }
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: data[0].data,
|
||||
lineWidth: 4,
|
||||
color: '#000000',
|
||||
name: data[0].name,
|
||||
zIndex: 2
|
||||
}, {
|
||||
data: data[1].data,
|
||||
lineWidth: 5,
|
||||
name: data[1].name,
|
||||
zIndex: 1,
|
||||
color: {
|
||||
pattern: {
|
||||
image: 'https://usetrmnl.com/images/grayscale/gray-5.png',
|
||||
width: 12,
|
||||
height: 12
|
||||
}
|
||||
}
|
||||
}],
|
||||
tooltip: { enabled: false },
|
||||
legend: { enabled: false },
|
||||
yAxis: { visible: false },
|
||||
xAxis: { visible: false },
|
||||
credits: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full b-h-gray-5"></div>
|
||||
<div class="grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xsmall value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:open_order_count]) %></span>
|
||||
<span class="label">Pending Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xsmall value--tnums" data-value-type="number"><%= number_with_delimiter(metrics[:closed_order_count]) %></span>
|
||||
<span class="label">Fulfilled Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full b-h-gray-5"></div>
|
||||
<div class="grid grid grid--cols-2">
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xxsmall value--tnums" data-value-type="date">
|
||||
<div class="w--12 h--1.5 mb--2 bg--gray-5"></div>
|
||||
<%= DateTime.parse(comparison_period.first).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(comparison_period.last).strftime("%b %d") %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="meta"></div>
|
||||
<div class="content">
|
||||
<span class="value value--xxsmall value--tnums" data-value-type="date">
|
||||
<div class="w--12 h--1.5 mb--2 bg--black" style="border-radius: 20px;"></div>
|
||||
<%= DateTime.parse(histogram[:current].first[:date]).strftime("%b %d") %> -
|
||||
<%= DateTime.parse(histogram[:current].last[:date]).strftime("%b %d") %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="title_bar">
|
||||
<img class="image" src="<%= Rails.application.credentials.base_url %>/images/plugins/shopify--render.svg" />
|
||||
<span class="title">Shopify</span>
|
||||
<span class="instance"><%= instance_name %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render partial: 'plugins/errors/quadrant', locals: {
|
||||
icon: 'shopify--render.svg',
|
||||
title: 'Shopify',
|
||||
instance: instance_name,
|
||||
error_message: metrics[:errors]
|
||||
} %>
|
||||
<% end %>
|
||||
Reference in New Issue
Block a user