Update dashboard icons
This commit is contained in:
@@ -1,11 +1,41 @@
|
||||
class SuperAdmin::DashboardController < SuperAdmin::ApplicationController
|
||||
include ActionView::Helpers::NumberHelper
|
||||
|
||||
ALLOWED_CHART_DAYS = [7, 30].freeze
|
||||
|
||||
def index
|
||||
@data = Conversation.unscoped.group_by_day(:created_at, range: 30.days.ago..2.seconds.ago).count.to_a
|
||||
@accounts_count = number_with_delimiter(Account.count)
|
||||
@users_count = number_with_delimiter(User.count)
|
||||
@inboxes_count = number_with_delimiter(Inbox.count)
|
||||
@conversations_count = number_with_delimiter(Conversation.count)
|
||||
@chart_days = parse_chart_days
|
||||
@data = conversations_by_day
|
||||
@accounts_count = format_count(Account.count)
|
||||
@users_count = format_count(User.count)
|
||||
@inboxes_count = format_count(Inbox.count)
|
||||
@conversations_count = format_count(recent_conversations.count)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_chart_days
|
||||
days = params[:chart_period].to_i
|
||||
ALLOWED_CHART_DAYS.include?(days) ? days : 7
|
||||
end
|
||||
|
||||
def chart_date_range
|
||||
(@chart_days - 1).days.ago.beginning_of_day..Time.current
|
||||
end
|
||||
|
||||
def last_30_days
|
||||
29.days.ago.beginning_of_day..Time.current
|
||||
end
|
||||
|
||||
def recent_conversations
|
||||
Conversation.unscoped.where(created_at: last_30_days)
|
||||
end
|
||||
|
||||
def conversations_by_day
|
||||
Conversation.unscoped.group_by_day(:created_at, range: chart_date_range, default_value: 0).count.to_a
|
||||
end
|
||||
|
||||
def format_count(count)
|
||||
number_with_delimiter(count)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,8 +31,8 @@ class AccountDashboard < Administrate::BaseDashboard
|
||||
updated_at: Field::DateTime,
|
||||
users: CountField,
|
||||
conversations: CountField,
|
||||
locale: Field::Select.with_options(collection: LANGUAGES_CONFIG.map { |_x, y| y[:iso_639_1_code] }),
|
||||
status: Field::Select.with_options(collection: [%w[Active active], %w[Suspended suspended]]),
|
||||
locale: LocaleField,
|
||||
status: StatusField,
|
||||
account_users: Field::HasMany,
|
||||
custom_attributes: Field::String
|
||||
}.merge(enterprise_attribute_types).freeze
|
||||
@@ -45,10 +45,9 @@ class AccountDashboard < Administrate::BaseDashboard
|
||||
COLLECTION_ATTRIBUTES = %i[
|
||||
id
|
||||
name
|
||||
locale
|
||||
users
|
||||
conversations
|
||||
status
|
||||
users
|
||||
locale
|
||||
].freeze
|
||||
|
||||
# SHOW_PAGE_ATTRIBUTES
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
require 'administrate/field/base'
|
||||
|
||||
class LocaleField < Administrate::Field::Base
|
||||
def to_s
|
||||
language = LANGUAGES_CONFIG.find { |_key, val| val[:iso_639_1_code] == data }
|
||||
language ? language[1][:name] : data
|
||||
end
|
||||
|
||||
def selectable_options
|
||||
LANGUAGES_CONFIG.map { |_key, val| [val[:name], val[:iso_639_1_code]] }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
require 'administrate/field/base'
|
||||
|
||||
class StatusField < Administrate::Field::Base
|
||||
def to_s
|
||||
data.to_s.capitalize
|
||||
end
|
||||
|
||||
def active?
|
||||
data.to_s == 'active'
|
||||
end
|
||||
|
||||
def selectable_options
|
||||
[%w[Active active], %w[Suspended suspended]]
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
/* eslint-disable vue/no-bare-strings-in-template */
|
||||
import { computed, ref } from 'vue';
|
||||
import BarChart from 'shared/components/charts/BarChart.vue';
|
||||
|
||||
const props = defineProps({
|
||||
componentData: {
|
||||
type: Object,
|
||||
@@ -8,11 +10,32 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const chartFilterOptions = [
|
||||
{ value: '7', label: 'Last 7d' },
|
||||
{ value: '30', label: 'Last 30d' },
|
||||
];
|
||||
|
||||
const selectedChartFilter = ref(
|
||||
props.componentData.chartDays?.toString() || '7'
|
||||
);
|
||||
|
||||
const onChartFilterChange = event => {
|
||||
const period = event.target.value;
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('chart_period', period);
|
||||
window.location.href = url.toString();
|
||||
};
|
||||
|
||||
const formatDate = dateString => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
};
|
||||
|
||||
const prepareData = sourceData => {
|
||||
var labels = [];
|
||||
var data = [];
|
||||
const labels = [];
|
||||
const data = [];
|
||||
sourceData.forEach(item => {
|
||||
labels.push(item[0]);
|
||||
labels.push(formatDate(item[0]));
|
||||
data.push(item[1]);
|
||||
});
|
||||
return {
|
||||
@@ -44,18 +67,19 @@ const { accountsCount, usersCount, inboxesCount, conversationsCount } =
|
||||
role="banner"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]"
|
||||
class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="border border-n-weak p-2 rounded-full text-n-slate-11"
|
||||
>
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24">
|
||||
<use xlink:href="#icon-dashboard-line" />
|
||||
<use xlink:href="#icon-layout-dashboard" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 id="page-title" class="text-base font-medium tracking-tight text-n-slate-12">
|
||||
<h1
|
||||
id="page-title"
|
||||
class="text-base font-medium tracking-tight text-n-slate-12"
|
||||
>
|
||||
Admin Dashboard
|
||||
</h1>
|
||||
<p class="text-xs text-n-slate-11 m-0">
|
||||
@@ -67,7 +91,7 @@ const { accountsCount, usersCount, inboxesCount, conversationsCount } =
|
||||
</header>
|
||||
|
||||
<main class="flex-1 px-6 overflow-y-auto">
|
||||
<div class="w-full mx-auto max-w-[60rem] py-4">
|
||||
<div class="w-full mx-auto max-w-[60rem] py-8">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||
<div
|
||||
class="p-4 rounded-xl bg-n-solid-2 outline outline-1 outline-n-container shadow-sm"
|
||||
@@ -99,14 +123,34 @@ const { accountsCount, usersCount, inboxesCount, conversationsCount } =
|
||||
<div class="text-3xl font-semibold text-n-slate-12">
|
||||
{{ conversationsCount }}
|
||||
</div>
|
||||
<div class="text-sm text-n-slate-11">Conversations</div>
|
||||
<div class="text-sm text-n-slate-11">Conversations (30d)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-6 rounded-xl bg-n-solid-2 outline outline-1 outline-n-container shadow-sm"
|
||||
class="px-6 pt-4 pb-6 rounded-xl bg-n-solid-2 outline outline-1 outline-n-container shadow-sm"
|
||||
>
|
||||
<BarChart class="w-full max-h-[500px]" :collection="chartData" />
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="text-base font-semibold text-n-slate-12">
|
||||
Conversations
|
||||
</div>
|
||||
<select
|
||||
v-model="selectedChartFilter"
|
||||
class="w-auto px-3 py-1.5 text-sm rounded-lg border border-n-weak bg-n-background text-n-slate-12 cursor-pointer focus:outline-none focus:ring-2 focus:ring-n-brand"
|
||||
@change="onChartFilterChange"
|
||||
>
|
||||
<option
|
||||
v-for="option in chartFilterOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="h-[300px]">
|
||||
<BarChart class="w-full h-full" :collection="chartData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= image_tag field.avatar_url if field.avatar_url.present? %>
|
||||
<%= image_tag field.avatar_url, class: "w-8 h-8 rounded-full" if field.avatar_url.present? %>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="flex flex-col gap-1">
|
||||
<%= f.label field.attribute, class: "text-sm font-medium text-n-slate-11" %>
|
||||
<%= f.select(
|
||||
field.attribute,
|
||||
options_for_select(field.selectable_options, field.data),
|
||||
{ include_blank: false },
|
||||
{ class: "h-9 px-3 min-w-[120px] text-sm text-n-slate-12 bg-n-solid-2 border border-n-weak rounded-lg focus:ring-2 focus:ring-woot-500 focus:border-woot-500 outline-none" }
|
||||
) %>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium bg-n-alpha-2 text-n-slate-11">
|
||||
<%= field.to_s %>
|
||||
</span>
|
||||
@@ -0,0 +1 @@
|
||||
<%= field.to_s %>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="flex flex-col gap-1">
|
||||
<%= f.label field.attribute, class: "text-sm font-medium text-n-slate-11" %>
|
||||
<%= f.select(
|
||||
field.attribute,
|
||||
options_for_select(field.selectable_options, field.data),
|
||||
{ include_blank: false },
|
||||
{ class: "h-9 px-3 min-w-[120px] text-sm text-n-slate-12 bg-n-solid-2 border border-n-weak rounded-lg focus:ring-2 focus:ring-woot-500 focus:border-woot-500 outline-none" }
|
||||
) %>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium <%= field.active? ? 'bg-n-teal-3 text-n-teal-11' : 'bg-n-ruby-3 text-n-ruby-11' %>">
|
||||
<%= field.to_s %>
|
||||
</span>
|
||||
@@ -0,0 +1,3 @@
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium <%= field.active? ? 'bg-n-teal-3 text-n-teal-11' : 'bg-n-ruby-3 text-n-ruby-11' %>">
|
||||
<%= field.to_s %>
|
||||
</span>
|
||||
@@ -21,10 +21,10 @@ end %>
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#icon-building-4-line" /></svg>
|
||||
<svg width="24" height="24"><use xlink:href="#icon-building-2" /></svg>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base font-medium tracking-tight text-n-slate-12">
|
||||
@@ -82,38 +82,38 @@ end %>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="bg-n-slate-1">
|
||||
<thead class="border-b border-n-weak">
|
||||
<tr>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Account</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">User</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Inviter</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Role</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11"></th>
|
||||
<th class="text-left py-3 !pl-6 pr-4 text-sm text-n-slate-11">Account</th>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11">User</th>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11">Inviter</th>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11">Role</th>
|
||||
<th class="text-left py-3 pl-4 !pr-6 text-sm text-n-slate-11"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% page.resource.account_users.each do |account_user| %>
|
||||
<tr class="hover:bg-n-alpha-1">
|
||||
<td class="py-3 px-6 text-sm text-n-slate-12">
|
||||
<td class="py-4 !pl-6 pr-4 text-sm text-n-slate-12">
|
||||
<%= link_to "##{account_user.account_id} #{account_user.account.name}", super_admin_account_path(account_user.account), class: "text-n-slate-12 hover:underline" %>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-sm text-n-slate-12">
|
||||
<td class="py-4 px-4 text-sm text-n-slate-12">
|
||||
<%= link_to "##{account_user.user_id} #{account_user.user.name}", super_admin_user_path(account_user.user), class: "text-n-slate-12 hover:underline" %>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-sm text-n-slate-11">
|
||||
<td class="py-4 px-4 text-sm text-n-slate-11">
|
||||
<% if account_user.inviter.present? %>
|
||||
<%= link_to account_user.inviter.name, super_admin_user_path(account_user.inviter), class: "text-n-slate-12 hover:underline" %>
|
||||
<% else %>
|
||||
<span class="text-n-slate-10">---</span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<td class="py-4 px-4">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium bg-n-alpha-2 text-n-slate-12 capitalize">
|
||||
<%= account_user.role %>
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-right">
|
||||
<%= link_to "Destroy", super_admin_account_user_path(account_user), method: :delete, data: { confirm: "Are you sure?" }, class: "text-sm text-ruby-500 hover:text-ruby-600 hover:underline" %>
|
||||
<td class="py-4 pl-4 !pr-6 text-right">
|
||||
<%= link_to "Destroy", super_admin_account_user_path(account_user), method: :delete, data: { confirm: "Are you sure?" }, class: "text-sm font-medium text-n-ruby-11 hover:underline" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#icon-settings-2-line" /></svg>
|
||||
<svg width="24" height="24"><use xlink:href="#icon-zap" /></svg>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base font-medium tracking-tight text-n-slate-12" id="page-title">
|
||||
|
||||
@@ -18,80 +18,80 @@ to display a collection of resources in an HTML table.
|
||||
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||
%>
|
||||
|
||||
<div class="rounded-xl bg-n-solid-2 shadow outline-1 outline outline-n-container overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full" aria-labelledby="<%= table_title %>">
|
||||
<thead class="bg-n-slate-1">
|
||||
<tr>
|
||||
<% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
|
||||
<th class="text-left py-3 px-4 text-sm font-medium text-n-slate-11"
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
||||
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
||||
), class: "inline-flex items-center gap-1 hover:text-n-slate-12") do %>
|
||||
<%= t(
|
||||
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||
default: attr_name.to_s,
|
||||
).titleize %>
|
||||
<% if collection_presenter.ordered_by?(attr_name) %>
|
||||
<span class="<%= collection_presenter.ordered_html_class(attr_name) == 'asc' ? '' : 'rotate-180' %>">
|
||||
<svg class="w-3 h-3" aria-hidden="true">
|
||||
<use xlink:href="#icon-up-caret" />
|
||||
</svg>
|
||||
</span>
|
||||
<% end %>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full" aria-labelledby="<%= table_title %>">
|
||||
<thead class="border-b border-n-weak">
|
||||
<tr>
|
||||
<% collection_presenter.attribute_types.each_with_index do |(attr_name, attr_type), index| %>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11 first:!pl-3 last:!pr-3"
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
||||
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
||||
), class: "inline-flex items-center gap-1 hover:text-n-slate-12") do %>
|
||||
<%= t(
|
||||
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||
default: attr_name.to_s,
|
||||
).titleize %>
|
||||
<% if collection_presenter.ordered_by?(attr_name) %>
|
||||
<span class="<%= collection_presenter.ordered_html_class(attr_name) == 'asc' ? '' : 'rotate-180' %>">
|
||||
<svg class="w-3 h-3" aria-hidden="true">
|
||||
<use xlink:href="#icon-up-caret" />
|
||||
</svg>
|
||||
</span>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
<% if existing_action?(collection_presenter.resource_name, :edit) %>
|
||||
<th scope="col"></th>
|
||||
<% end %>
|
||||
<% if existing_action?(collection_presenter.resource_name, :destroy) %>
|
||||
<th scope="col" class="pr-3"></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% resources.each do |resource| %>
|
||||
<tr class="js-table-row hover:bg-n-alpha-1 transition-colors"
|
||||
tabindex="0"
|
||||
<% if existing_action? collection_presenter.resource_name, :show %>
|
||||
<%= %(role=link data-url=#{polymorphic_path([namespace, resource])}) %>
|
||||
<% end %>
|
||||
>
|
||||
<% collection_presenter.attributes_for(resource).each_with_index do |attribute, index| %>
|
||||
<td class="py-4 px-4 text-sm text-n-slate-12 first:!pl-3 last:!pr-3">
|
||||
<% if authorized_action? resource, :show -%>
|
||||
<a href="<%= polymorphic_path([namespace, resource]) -%>"
|
||||
class="hover:underline"
|
||||
>
|
||||
<%= render_field attribute %>
|
||||
</a>
|
||||
<% end -%>
|
||||
</td>
|
||||
<% end %>
|
||||
<% [existing_action?(collection_presenter.resource_name, :edit),
|
||||
existing_action?(collection_presenter.resource_name, :destroy)].count(true).times do %>
|
||||
<th scope="col" class="py-3 px-4"></th>
|
||||
|
||||
<% if existing_action?(collection_presenter.resource_name, :edit) %>
|
||||
<td class="py-4 px-4 text-right"><%= link_to(
|
||||
t("administrate.actions.edit"),
|
||||
[:edit, namespace, resource],
|
||||
class: "text-sm font-medium text-n-brand hover:underline",
|
||||
) if authorized_action? resource, :edit%></td>
|
||||
<% end %>
|
||||
|
||||
<% if existing_action?(collection_presenter.resource_name, :destroy) %>
|
||||
<td class="py-4 px-4 !pr-3 text-right"><%= link_to(
|
||||
t("administrate.actions.destroy"),
|
||||
[namespace, resource],
|
||||
class: "text-sm font-medium text-n-ruby-11 hover:underline",
|
||||
method: :delete,
|
||||
data: { confirm: t("administrate.actions.confirm") }
|
||||
) if authorized_action? resource, :destroy %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% resources.each do |resource| %>
|
||||
<tr class="js-table-row hover:bg-n-alpha-1 transition-colors"
|
||||
tabindex="0"
|
||||
<% if existing_action? collection_presenter.resource_name, :show %>
|
||||
<%= %(role=link data-url=#{polymorphic_path([namespace, resource])}) %>
|
||||
<% end %>
|
||||
>
|
||||
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
||||
<td class="py-3 px-4 text-sm text-n-slate-12">
|
||||
<% if authorized_action? resource, :show -%>
|
||||
<a href="<%= polymorphic_path([namespace, resource]) -%>"
|
||||
class="hover:underline"
|
||||
>
|
||||
<%= render_field attribute %>
|
||||
</a>
|
||||
<% end -%>
|
||||
</td>
|
||||
<% end %>
|
||||
|
||||
<% if existing_action? collection_presenter.resource_name, :edit %>
|
||||
<td class="py-3 px-4 text-right"><%= link_to(
|
||||
t("administrate.actions.edit"),
|
||||
[:edit, namespace, resource],
|
||||
class: "text-sm text-woot-500 hover:text-woot-600 hover:underline",
|
||||
) if authorized_action? resource, :edit%></td>
|
||||
<% end %>
|
||||
|
||||
<% if existing_action? collection_presenter.resource_name, :destroy %>
|
||||
<td class="py-3 px-4 text-right"><%= link_to(
|
||||
t("administrate.actions.destroy"),
|
||||
[namespace, resource],
|
||||
class: "text-sm text-ruby-500 hover:text-ruby-600 hover:underline",
|
||||
method: :delete,
|
||||
data: { confirm: t("administrate.actions.confirm") }
|
||||
) if authorized_action? resource, :destroy %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -177,4 +177,65 @@
|
||||
<symbol id="icon-slack" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M6.527 14.514A1.973 1.973 0 0 1 4.56 16.48a1.973 1.973 0 0 1-1.968-1.967c0-1.083.885-1.968 1.968-1.968h1.967zm.992 0c0-1.083.885-1.968 1.968-1.968s1.967.885 1.967 1.968v4.927a1.973 1.973 0 0 1-1.967 1.968a1.973 1.973 0 0 1-1.968-1.968zm1.968-7.987A1.973 1.973 0 0 1 7.519 4.56c0-1.083.885-1.967 1.968-1.967s1.967.884 1.967 1.967v1.968zm0 .992c1.083 0 1.967.884 1.967 1.967a1.973 1.973 0 0 1-1.967 1.968H4.56a1.973 1.973 0 0 1-1.968-1.968c0-1.083.885-1.967 1.968-1.967zm7.986 1.967c0-1.083.885-1.967 1.968-1.967s1.968.884 1.968 1.967a1.973 1.973 0 0 1-1.968 1.968h-1.968zm-.991 0a1.973 1.973 0 0 1-1.968 1.968a1.973 1.973 0 0 1-1.968-1.968V4.56c0-1.083.885-1.967 1.968-1.967s1.968.884 1.968 1.967zm-1.968 7.987c1.083 0 1.968.885 1.968 1.968a1.973 1.973 0 0 1-1.968 1.968a1.973 1.973 0 0 1-1.968-1.968v-1.968zm0-.992a1.973 1.973 0 0 1-1.968-1.967c0-1.083.885-1.968 1.968-1.968h4.927c1.083 0 1.968.885 1.968 1.968a1.973 1.973 0 0 1-1.968 1.967z"/>
|
||||
</symbol>
|
||||
|
||||
<!-- Lucide-style icons for sidebar -->
|
||||
<symbol id="icon-layout-dashboard" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="7" height="9" x="3" y="3" rx="1"/>
|
||||
<rect width="7" height="5" x="14" y="3" rx="1"/>
|
||||
<rect width="7" height="9" x="14" y="12" rx="1"/>
|
||||
<rect width="7" height="5" x="3" y="16" rx="1"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-building-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z"/>
|
||||
<path d="M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"/>
|
||||
<path d="M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2"/>
|
||||
<path d="M10 6h4"/>
|
||||
<path d="M10 10h4"/>
|
||||
<path d="M10 14h4"/>
|
||||
<path d="M10 18h4"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="9" cy="7" r="4"/>
|
||||
<path d="M22 21v-2a4 4 0 0 0-3-3.87"/>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-toy-brick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="18" height="12" x="3" y="8" rx="1"/>
|
||||
<path d="M10 8V5c0-.6-.4-1-1-1H6a1 1 0 0 0-1 1v3"/>
|
||||
<path d="M19 8V5c0-.6-.4-1-1-1h-3a1 1 0 0 0-1 1v3"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-bot" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 8V4H8"/>
|
||||
<rect width="16" height="12" x="4" y="8" rx="2"/>
|
||||
<path d="M2 14h2"/>
|
||||
<path d="M20 14h2"/>
|
||||
<path d="M15 13v2"/>
|
||||
<path d="M9 13v2"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-zap" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-layers" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/>
|
||||
<path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/>
|
||||
<path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-heart-pulse" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>
|
||||
<path d="M3.22 12H9.5l.5-1 2 4.5 2-7 1.5 3.5h5.27"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="icon-log-out" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
|
||||
<polyline points="16 17 21 12 16 7"/>
|
||||
<line x1="21" x2="9" y1="12" y2="12"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 46 KiB |
@@ -1,9 +1,9 @@
|
||||
<li>
|
||||
<% is_active = current_page?(url) %>
|
||||
<% base_class = "flex items-center gap-2 px-2.5 py-2 text-sm rounded-lg transition-colors" %>
|
||||
<% active_class = is_active ? "text-woot-500 bg-woot-50" : "text-n-slate-11 hover:text-n-slate-12 hover:bg-n-alpha-1" %>
|
||||
<% base_class = "flex items-center gap-2 px-2 py-1.5 h-8 text-sm font-medium rounded-lg" %>
|
||||
<% active_class = is_active ? "text-n-blue-text bg-n-alpha-2" : "text-n-slate-11 hover:bg-n-alpha-2" %>
|
||||
<%= link_to(url, class: "#{base_class} #{active_class}") do %>
|
||||
<svg width="20" height="20" class="flex-shrink-0"><use xlink:href="#<%= icon %>" /></svg>
|
||||
<svg width="16" height="16" class="flex-shrink-0"><use xlink:href="#<%= icon %>" /></svg>
|
||||
<span><%= label %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
@@ -12,16 +12,16 @@ as defined by the routes in the `admin/` namespace
|
||||
|
||||
<%
|
||||
sidebar_icons = {
|
||||
accounts: 'icon-building-4-line',
|
||||
users: 'icon-user-follow-line',
|
||||
platform_apps: 'icon-apps-2-line',
|
||||
agent_bots: 'icon-robot-line',
|
||||
accounts: 'icon-building-2',
|
||||
users: 'icon-users',
|
||||
platform_apps: 'icon-toy-brick',
|
||||
agent_bots: 'icon-bot',
|
||||
}
|
||||
%>
|
||||
|
||||
<div class="border-n-weak border-r w-56 flex-shrink-0 justify-between h-full flex flex-col bg-n-background" role="navigation">
|
||||
<div class="border-n-weak border-r w-[200px] flex-shrink-0 justify-between h-full flex flex-col bg-n-background" role="navigation">
|
||||
<div>
|
||||
<div class="flex items-center mx-4 border-n-weak border-b py-5">
|
||||
<div class="flex items-center px-4 border-n-weak border-b h-16 mt-px">
|
||||
<%= link_to image_tag('/brand-assets/logo_thumbnail.svg', alt: 'Chatwoot Admin Dashboard', class: 'h-8'), super_admin_root_url %>
|
||||
<div class="flex flex-col ml-2.5">
|
||||
<div class="text-sm font-medium text-n-slate-12">Chatwoot <%= Chatwoot.config[:version] %></div>
|
||||
@@ -30,7 +30,7 @@ as defined by the routes in the `admin/` namespace
|
||||
</div>
|
||||
|
||||
<ul class="py-4 px-3 space-y-0.5">
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-dashboard-line', url: super_admin_root_url, label: 'Dashboard' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-layout-dashboard', url: super_admin_root_url, label: 'Dashboard' } %>
|
||||
<% Administrate::Namespace.new(namespace).resources.each do |resource| %>
|
||||
<% next if ["account_users", "access_tokens", "installation_configs", "dashboard", "devise/sessions", "app_configs", "instance_statuses", "settings"].include? resource.resource %>
|
||||
<%= render partial: "nav_item", locals: {
|
||||
@@ -46,10 +46,10 @@ as defined by the routes in the `admin/` namespace
|
||||
</div>
|
||||
<div class="border-t border-n-weak">
|
||||
<ul class="py-4 px-3 space-y-0.5">
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-mist-fill', url: sidekiq_web_url, label: 'Sidekiq Dashboard' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-health-book-line', url: super_admin_instance_status_url, label: 'Instance Health' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-grid-line', url: '/', label: 'Agent Dashboard' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-logout-circle-r-line', url: super_admin_logout_url, label: 'Logout' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-layers', url: sidekiq_web_url, label: 'Sidekiq Dashboard' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-heart-pulse', url: super_admin_instance_status_url, label: 'Instance Health' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-layout-dashboard', url: '/', label: 'Agent Dashboard' } %>
|
||||
<%= render partial: "nav_item", locals: { icon: 'icon-log-out', url: super_admin_logout_url, label: 'Logout' } %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<li>
|
||||
<details class="group" <%= 'open' if open %>>
|
||||
<summary class="flex items-center gap-2 px-2.5 py-2 text-sm rounded-lg transition-colors cursor-pointer list-none <%= open ? 'text-woot-500 bg-woot-50' : 'text-n-slate-11 hover:text-n-slate-12 hover:bg-n-alpha-1' %>">
|
||||
<summary class="flex items-center gap-2 px-2 py-1.5 h-8 text-sm font-medium rounded-lg cursor-pointer list-none <%= open ? 'text-n-slate-12' : 'text-n-slate-11 hover:bg-n-alpha-2' %>">
|
||||
<%= link_to super_admin_settings_url, class: 'flex items-center gap-2 flex-1' do %>
|
||||
<svg width="20" height="20" class="flex-shrink-0"><use xlink:href="#icon-settings-2-line" /></svg>
|
||||
<svg width="16" height="16" class="flex-shrink-0"><use xlink:href="#icon-zap" /></svg>
|
||||
<span>Settings</span>
|
||||
<% end %>
|
||||
<svg class="w-4 h-4 transition-transform duration-200 group-open:rotate-180" viewBox="0 0 20 20" fill="currentColor">
|
||||
<svg class="w-3 h-3 transition-transform duration-200 group-open:rotate-180" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</summary>
|
||||
<ul class="mt-1 ml-7 space-y-0.5">
|
||||
<ul class="grid m-0 list-none">
|
||||
<% settings_pages.each do |_feature_key, attrs| %>
|
||||
<% url = super_admin_app_config_url(config: attrs['config_key']) %>
|
||||
<li>
|
||||
<li class="py-0.5 pl-3 ml-3 relative before:content-[''] before:absolute before:w-0.5 before:h-full before:bg-n-slate-4 before:left-0 before:rounded-full first:before:rounded-t last:before:h-1/2 last:before:top-0">
|
||||
<% is_active = current_page?(url) %>
|
||||
<% base_class = "flex items-center px-2.5 py-1.5 text-sm rounded-lg transition-colors" %>
|
||||
<% active_class = is_active ? "text-woot-500 bg-woot-50" : "text-n-slate-11 hover:text-n-slate-12 hover:bg-n-alpha-1" %>
|
||||
<%= link_to url, class: "#{base_class} #{active_class}" do %>
|
||||
<span><%= attrs['name'] %></span>
|
||||
<%= link_to url, class: "flex h-8 items-center gap-2 px-2 py-1 rounded-lg text-sm hover:bg-n-alpha-2 #{is_active ? 'text-n-blue-text bg-n-alpha-2' : 'text-n-slate-11'}" do %>
|
||||
<span class="truncate"><%= attrs['name'] %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
@@ -42,7 +42,7 @@ It renders the `_table` partial to display details about the resources.
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#<%= current_icon %>" /></svg>
|
||||
|
||||
@@ -33,7 +33,7 @@ as well as a link to its edit page.
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#<%= current_icon %>" /></svg>
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
usersCount: @users_count,
|
||||
inboxesCount: @inboxes_count,
|
||||
conversationsCount: @conversations_count,
|
||||
chartData: @data
|
||||
chartData: @data,
|
||||
chartDays: @chart_days
|
||||
}) %>
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#icon-health-book-line" /></svg>
|
||||
<svg width="24" height="24"><use xlink:href="#icon-heart-pulse" /></svg>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base font-medium tracking-tight text-n-slate-12" id="page-title">
|
||||
@@ -27,17 +27,17 @@
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="bg-n-slate-1">
|
||||
<thead class="border-b border-n-weak">
|
||||
<tr>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Metric</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Value</th>
|
||||
<th class="text-left py-3 pl-8 pr-4 text-sm text-n-slate-11">Metric</th>
|
||||
<th class="text-left py-3 pl-4 pr-8 text-sm text-n-slate-11">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% @metrics.each do |key, value| %>
|
||||
<tr class="hover:bg-n-alpha-1">
|
||||
<td class="py-3 px-6 text-sm text-n-slate-12 font-medium"><%= key %></td>
|
||||
<td class="py-3 px-6 text-sm text-n-slate-11 font-mono"><%= value %></td>
|
||||
<td class="py-4 pl-8 pr-4 text-sm text-n-slate-12 font-medium"><%= key %></td>
|
||||
<td class="py-4 pl-4 pr-8 text-sm text-n-slate-11 font-mono"><%= value %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center w-full h-20 mx-auto max-w-[60rem] gap-3">
|
||||
<div class="flex items-center w-full h-16 mx-auto max-w-[60rem] gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#icon-settings-2-line" /></svg>
|
||||
<svg width="24" height="24"><use xlink:href="#icon-zap" /></svg>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base font-medium tracking-tight text-n-slate-12" id="page-title">
|
||||
|
||||
@@ -18,80 +18,79 @@ to display a collection of resources in an HTML table.
|
||||
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||
%>
|
||||
|
||||
<div class="rounded-xl bg-n-solid-2 shadow outline-1 outline outline-n-container overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full" aria-labelledby="<%= table_title %>">
|
||||
<thead class="bg-n-slate-1">
|
||||
<tr>
|
||||
<% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
|
||||
<th class="text-left py-3 px-4 text-sm font-medium text-n-slate-11"
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
||||
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
||||
), class: "inline-flex items-center gap-1 hover:text-n-slate-12") do %>
|
||||
<%= t(
|
||||
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||
default: attr_name.to_s,
|
||||
).titleize %>
|
||||
<% if collection_presenter.ordered_by?(attr_name) %>
|
||||
<span class="<%= collection_presenter.ordered_html_class(attr_name) == 'asc' ? '' : 'rotate-180' %>">
|
||||
<svg class="w-3 h-3" aria-hidden="true">
|
||||
<use xlink:href="#icon-up-caret" />
|
||||
</svg>
|
||||
</span>
|
||||
<% end %>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full" aria-labelledby="<%= table_title %>">
|
||||
<thead class="border-b border-n-weak">
|
||||
<tr>
|
||||
<% collection_presenter.attribute_types.each_with_index do |(attr_name, attr_type), index| %>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11 first:!pl-3 last:!pr-3"
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||
<%= link_to(sanitized_order_params(page, collection_field_name).merge(
|
||||
collection_presenter.order_params_for(attr_name, key: collection_field_name)
|
||||
), class: "inline-flex items-center gap-1 hover:text-n-slate-12") do %>
|
||||
<%= t(
|
||||
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||
default: attr_name.to_s,
|
||||
).titleize %>
|
||||
<% if collection_presenter.ordered_by?(attr_name) %>
|
||||
<span class="<%= collection_presenter.ordered_html_class(attr_name) == 'asc' ? '' : 'rotate-180' %>">
|
||||
<svg class="w-3 h-3" aria-hidden="true">
|
||||
<use xlink:href="#icon-up-caret" />
|
||||
</svg>
|
||||
</span>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
</th>
|
||||
<% end %>
|
||||
<% if existing_action?(collection_presenter.resource_name, :edit) %>
|
||||
<th scope="col"></th>
|
||||
<% end %>
|
||||
<% if existing_action?(collection_presenter.resource_name, :destroy) %>
|
||||
<th scope="col" class="pr-3"></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% resources.each do |resource| %>
|
||||
<tr class="js-table-row hover:bg-n-alpha-1 transition-colors"
|
||||
tabindex="0"
|
||||
<% if existing_action? collection_presenter.resource_name, :show %>
|
||||
<%= %(role=link data-url=#{polymorphic_path([namespace, resource.becomes(User)])}) %>
|
||||
<% end %>
|
||||
>
|
||||
<% collection_presenter.attributes_for(resource).each_with_index do |attribute, index| %>
|
||||
<td class="py-4 px-4 text-sm text-n-slate-12 first:!pl-3 last:!pr-3">
|
||||
<% if authorized_action? resource, :show -%>
|
||||
<a href="<%= polymorphic_path([namespace, resource.becomes(User)]) -%>"
|
||||
class="hover:underline"
|
||||
>
|
||||
<%= render_field attribute %>
|
||||
</a>
|
||||
<% end -%>
|
||||
</td>
|
||||
<% end %>
|
||||
<% [existing_action?(collection_presenter.resource_name, :edit),
|
||||
existing_action?(collection_presenter.resource_name, :destroy)].count(true).times do %>
|
||||
<th scope="col" class="py-3 px-4"></th>
|
||||
<% if existing_action?(collection_presenter.resource_name, :edit) %>
|
||||
<td class="py-4 px-4 text-right"><%= link_to(
|
||||
t("administrate.actions.edit"),
|
||||
[:edit, namespace, resource.becomes(User)],
|
||||
class: "text-sm font-medium text-n-brand hover:underline",
|
||||
) if authorized_action? resource, :edit%></td>
|
||||
<% end %>
|
||||
|
||||
<% if existing_action?(collection_presenter.resource_name, :destroy) %>
|
||||
<td class="py-4 pl-4 !pr-3 text-right"><%= link_to(
|
||||
t("administrate.actions.destroy"),
|
||||
[namespace, collection_presenter.resource_name == 'account_user' ? resource : resource.becomes(User)],
|
||||
class: "text-sm font-medium text-n-ruby-11 hover:underline",
|
||||
method: :delete,
|
||||
data: { confirm: t("administrate.actions.confirm") }
|
||||
) if authorized_action? resource, :destroy %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% resources.each do |resource| %>
|
||||
<tr class="js-table-row hover:bg-n-alpha-1 transition-colors"
|
||||
tabindex="0"
|
||||
<% if existing_action? collection_presenter.resource_name, :show %>
|
||||
<%= %(role=link data-url=#{polymorphic_path([namespace, resource.becomes(User)])}) %>
|
||||
<% end %>
|
||||
>
|
||||
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
||||
<td class="py-3 px-4 text-sm text-n-slate-12">
|
||||
<% if authorized_action? resource, :show -%>
|
||||
<a href="<%= polymorphic_path([namespace, resource.becomes(User)]) -%>"
|
||||
class="hover:underline"
|
||||
>
|
||||
<%= render_field attribute %>
|
||||
</a>
|
||||
<% end -%>
|
||||
</td>
|
||||
<% end %>
|
||||
|
||||
<% if existing_action? collection_presenter.resource_name, :edit %>
|
||||
<td class="py-3 px-4 text-right"><%= link_to(
|
||||
t("administrate.actions.edit"),
|
||||
[:edit, namespace, resource.becomes(User)],
|
||||
class: "text-sm text-woot-500 hover:text-woot-600 hover:underline",
|
||||
) if authorized_action? resource, :edit%></td>
|
||||
<% end %>
|
||||
|
||||
<% if existing_action? collection_presenter.resource_name, :destroy %>
|
||||
<td class="py-3 px-4 text-right"><%= link_to(
|
||||
t("administrate.actions.destroy"),
|
||||
[namespace, collection_presenter.resource_name == 'account_user' ? resource : resource.becomes(User)],
|
||||
class: "text-sm text-ruby-500 hover:text-ruby-600 hover:underline",
|
||||
method: :delete,
|
||||
data: { confirm: t("administrate.actions.confirm") }
|
||||
) if authorized_action? resource, :destroy %></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -20,10 +20,10 @@ as well as a link to its edit page.
|
||||
|
||||
<section class="flex flex-col w-full h-full">
|
||||
<header class="sticky top-0 z-10 bg-n-background border-b border-n-weak" role="banner">
|
||||
<div class="flex items-center justify-between w-full h-20 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center justify-between w-full h-16 mx-auto max-w-[60rem]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="border border-n-weak p-2 rounded-full text-n-slate-11">
|
||||
<svg width="24" height="24"><use xlink:href="#icon-user-follow-line" /></svg>
|
||||
<svg width="24" height="24"><use xlink:href="#icon-users" /></svg>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center">
|
||||
<h1 class="text-base font-medium tracking-tight text-n-slate-12">
|
||||
@@ -85,38 +85,38 @@ as well as a link to its edit page.
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="bg-n-slate-1">
|
||||
<thead class="border-b border-n-weak">
|
||||
<tr>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Account</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">User</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Inviter</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11">Role</th>
|
||||
<th class="text-left py-3 px-6 text-sm font-medium text-n-slate-11"></th>
|
||||
<th class="text-left py-3 !pl-6 pr-4 text-sm text-n-slate-11">Account</th>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11">User</th>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11">Inviter</th>
|
||||
<th class="text-left py-3 px-4 text-sm text-n-slate-11">Role</th>
|
||||
<th class="text-left py-3 pl-4 !pr-6 text-sm text-n-slate-11"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-n-weak">
|
||||
<% page.resource.account_users.each do |account_user| %>
|
||||
<tr class="hover:bg-n-alpha-1">
|
||||
<td class="py-3 px-6 text-sm text-n-slate-12">
|
||||
<td class="py-4 !pl-6 pr-4 text-sm text-n-slate-12">
|
||||
<%= link_to "##{account_user.account_id} #{account_user.account.name}", super_admin_account_path(account_user.account), class: "text-n-slate-12 hover:underline" %>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-sm text-n-slate-12">
|
||||
<td class="py-4 px-4 text-sm text-n-slate-12">
|
||||
<%= link_to "##{account_user.user_id} #{account_user.user.name}", super_admin_user_path(account_user.user), class: "text-n-slate-12 hover:underline" %>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-sm text-n-slate-11">
|
||||
<td class="py-4 px-4 text-sm text-n-slate-11">
|
||||
<% if account_user.inviter.present? %>
|
||||
<%= link_to account_user.inviter.name, super_admin_user_path(account_user.inviter), class: "text-n-slate-12 hover:underline" %>
|
||||
<% else %>
|
||||
<span class="text-n-slate-10">---</span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<td class="py-4 px-4">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium bg-n-alpha-2 text-n-slate-12 capitalize">
|
||||
<%= account_user.role %>
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-right">
|
||||
<%= link_to "Destroy", super_admin_account_user_path(account_user), method: :delete, data: { confirm: "Are you sure?" }, class: "text-sm text-ruby-500 hover:text-ruby-600 hover:underline" %>
|
||||
<td class="py-4 pl-4 !pr-6 text-right">
|
||||
<%= link_to "Destroy", super_admin_account_user_path(account_user), method: :delete, data: { confirm: "Are you sure?" }, class: "text-sm font-medium text-n-ruby-11 hover:underline" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user