chore: Clean up the feature presentation in super admin (#10949)

- improve styling of the feature toggle displayed in super admin

<img width="1540" alt="Screenshot 2025-02-21 at 4 47 58 PM"
src="https://github.com/user-attachments/assets/c111ff0d-c2ce-4609-832b-0ea631c97471"
/>

<img width="1451" alt="Screenshot 2025-02-21 at 4 48 11 PM"
src="https://github.com/user-attachments/assets/fe5df4ae-984b-4f9d-baba-9555935fbd55"
/>

---------

Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
Sojan Jose
2025-02-21 17:01:36 -08:00
committed by GitHub
parent 161024db9d
commit 19cfd4be4c
9 changed files with 512 additions and 143 deletions

View File

@@ -7,13 +7,46 @@ module SuperAdmin::AccountFeaturesHelper
account_features.filter { |feature| feature['premium'] }.pluck('name')
end
# Accepts account.features as argument
def self.filtered_features(features)
deployment_env = GlobalConfig.get_value('DEPLOYMENT_ENV')
return features if deployment_env == 'cloud'
# Returns a hash mapping feature names to their display names
def self.feature_display_names
account_features.each_with_object({}) do |feature, hash|
hash[feature['name']] = feature['display_name']
end
end
def self.filter_internal_features(features)
return features if GlobalConfig.get_value('DEPLOYMENT_ENV') == 'cloud'
# Filter out internal features for non-cloud environments
internal_features = account_features.select { |f| f['chatwoot_internal'] }.pluck('name')
features.except(*internal_features)
end
def self.filter_deprecated_features(features)
deprecated_features = account_features.select { |f| f['deprecated'] }.pluck('name')
features.except(*deprecated_features)
end
def self.sort_and_transform_features(features, display_names)
features.sort_by { |key, _| display_names[key] || key }
.to_h
.transform_keys { |key| [key, display_names[key]] }
end
def self.partition_features(features)
filtered = filter_internal_features(features)
filtered = filter_deprecated_features(filtered)
display_names = feature_display_names
regular, premium = filtered.partition { |key, _value| account_premium_features.exclude?(key) }
[
sort_and_transform_features(regular, display_names),
sort_and_transform_features(premium, display_names)
]
end
def self.filtered_features(features)
regular, premium = partition_features(features)
regular.merge(premium)
end
end

View File

@@ -6,3 +6,209 @@
body {
font-family: Inter, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important;
}
@layer base {
// FIXME: Use a common color file for all packs
// scss-lint:disable PropertySortOrder
:root {
--slate-1: 252 252 253;
--slate-2: 249 249 251;
--slate-3: 240 240 243;
--slate-4: 232 232 236;
--slate-5: 224 225 230;
--slate-6: 217 217 224;
--slate-7: 205 206 214;
--slate-8: 185 187 198;
--slate-9: 139 141 152;
--slate-10: 128 131 141;
--slate-11: 96 100 108;
--slate-12: 28 32 36;
--iris-1: 253 253 255;
--iris-2: 248 248 255;
--iris-3: 240 241 254;
--iris-4: 230 231 255;
--iris-5: 218 220 255;
--iris-6: 203 205 255;
--iris-7: 184 186 248;
--iris-8: 155 158 240;
--iris-9: 91 91 214;
--iris-10: 81 81 205;
--iris-11: 87 83 198;
--iris-12: 39 41 98;
--ruby-1: 255 252 253;
--ruby-2: 255 247 248;
--ruby-3: 254 234 237;
--ruby-4: 255 220 225;
--ruby-5: 255 206 214;
--ruby-6: 248 191 200;
--ruby-7: 239 172 184;
--ruby-8: 229 146 163;
--ruby-9: 229 70 102;
--ruby-10: 220 59 93;
--ruby-11: 202 36 77;
--ruby-12: 100 23 43;
--amber-1: 254 253 251;
--amber-2: 254 251 233;
--amber-3: 255 247 194;
--amber-4: 255 238 156;
--amber-5: 251 229 119;
--amber-6: 243 214 115;
--amber-7: 233 193 98;
--amber-8: 226 163 54;
--amber-9: 255 197 61;
--amber-10: 255 186 24;
--amber-11: 171 100 0;
--amber-12: 79 52 34;
--teal-1: 250 254 253;
--teal-2: 243 251 249;
--teal-3: 224 248 243;
--teal-4: 204 243 234;
--teal-5: 184 234 224;
--teal-6: 161 222 210;
--teal-7: 131 205 193;
--teal-8: 83 185 171;
--teal-9: 18 165 148;
--teal-10: 13 155 138;
--teal-11: 0 133 115;
--teal-12: 13 61 56;
--gray-1: 252 252 252;
--gray-2: 249 249 249;
--gray-3: 240 240 240;
--gray-4: 232 232 232;
--gray-5: 224 224 224;
--gray-6: 217 217 217;
--gray-7: 206 206 206;
--gray-8: 187 187 187;
--gray-9: 141 141 141;
--gray-10: 131 131 131;
--gray-11: 100 100 100;
--gray-12: 32 32 32;
--background-color: 253 253 253;
--text-blue: 8 109 224;
--border-container: 236 236 236;
--border-strong: 235 235 235;
--border-weak: 234 234 234;
--solid-1: 255 255 255;
--solid-2: 255 255 255;
--solid-3: 255 255 255;
--solid-active: 255 255 255;
--solid-amber: 252 232 193;
--solid-blue: 218 236 255;
--solid-iris: 230 231 255;
--alpha-1: 67, 67, 67, 0.06;
--alpha-2: 201, 202, 207, 0.15;
--alpha-3: 255, 255, 255, 0.96;
--black-alpha-1: 0, 0, 0, 0.12;
--black-alpha-2: 0, 0, 0, 0.04;
--border-blue: 39, 129, 246, 0.5;
--white-alpha: 255, 255, 255, 0.8;
}
.dark {
--slate-1: 17 17 19;
--slate-2: 24 25 27;
--slate-3: 33 34 37;
--slate-4: 39 42 45;
--slate-5: 46 49 53;
--slate-6: 54 58 63;
--slate-7: 67 72 78;
--slate-8: 90 97 105;
--slate-9: 105 110 119;
--slate-10: 119 123 132;
--slate-11: 176 180 186;
--slate-12: 237 238 240;
--iris-1: 19 19 30;
--iris-2: 23 22 37;
--iris-3: 32 34 72;
--iris-4: 38 42 101;
--iris-5: 48 51 116;
--iris-6: 61 62 130;
--iris-7: 74 74 149;
--iris-8: 89 88 177;
--iris-9: 91 91 214;
--iris-10: 84 114 228;
--iris-11: 158 177 255;
--iris-12: 224 223 254;
--ruby-1: 25 17 19;
--ruby-2: 30 21 23;
--ruby-3: 58 20 30;
--ruby-4: 78 19 37;
--ruby-5: 94 26 46;
--ruby-6: 111 37 57;
--ruby-7: 136 52 71;
--ruby-8: 179 68 90;
--ruby-9: 229 70 102;
--ruby-10: 236 90 114;
--ruby-11: 255 148 157;
--ruby-12: 254 210 225;
--amber-1: 22 18 12;
--amber-2: 29 24 15;
--amber-3: 48 32 8;
--amber-4: 63 39 0;
--amber-5: 77 48 0;
--amber-6: 92 61 5;
--amber-7: 113 79 25;
--amber-8: 143 100 36;
--amber-9: 255 197 61;
--amber-10: 255 214 10;
--amber-11: 255 202 22;
--amber-12: 255 231 179;
--teal-1: 13 21 20;
--teal-2: 17 28 27;
--teal-3: 13 45 42;
--teal-4: 2 59 55;
--teal-5: 8 72 67;
--teal-6: 20 87 80;
--teal-7: 28 105 97;
--teal-8: 32 126 115;
--teal-9: 18 165 148;
--teal-10: 14 179 158;
--teal-11: 11 216 182;
--teal-12: 173 240 221;
--gray-1: 17 17 17;
--gray-2: 25 25 25;
--gray-3: 34 34 34;
--gray-4: 42 42 42;
--gray-5: 49 49 49;
--gray-6: 58 58 58;
--gray-7: 72 72 72;
--gray-8: 96 96 96;
--gray-9: 110 110 110;
--gray-10: 123 123 123;
--gray-11: 180 180 180;
--gray-12: 238 238 238;
--background-color: 18 18 19;
--border-strong: 52 52 52;
--border-weak: 38 38 42;
--solid-1: 23 23 26;
--solid-2: 29 30 36;
--solid-3: 44 45 54;
--solid-active: 53 57 66;
--solid-amber: 42 37 30;
--solid-blue: 16 49 91;
--solid-iris: 38 42 101;
--text-blue: 126 182 255;
--alpha-1: 36, 36, 36, 0.8;
--alpha-2: 139, 147, 182, 0.15;
--alpha-3: 36, 38, 45, 0.9;
--black-alpha-1: 0, 0, 0, 0.3;
--black-alpha-2: 0, 0, 0, 0.2;
--border-blue: 39, 129, 246, 0.5;
--border-container: 236, 236, 236, 0;
--white-alpha: 255, 255, 255, 0.1;
}
}

View File

@@ -2,17 +2,33 @@
<%= f.label field.attribute %>
</div>
<div class="field-unit__field feature-container">
<% SuperAdmin::AccountFeaturesHelper.filtered_features(field.data).each do |key, val| %>
<div class='feature-cell'>
<% is_premium = SuperAdmin::AccountFeaturesHelper.account_premium_features.include? key %>
<% if is_premium %>
<span class='icon-container'>
<svg class="inline" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><path d="M480 224l-186.828 7.487L401.688 64l-59.247-32L256 208 169.824 32l-59.496 32 108.5 167.487L32 224v64l185.537-10.066L113.65 448l55.969 32L256 304l86.381 176 55.949-32-103.867-170.066L480 288z" fill="currentColor"/></svg>
<% regular_features, premium_features = SuperAdmin::AccountFeaturesHelper.filtered_features(field.data).partition { |key_array, _val| !SuperAdmin::AccountFeaturesHelper.account_premium_features.include?(key_array.first) } %>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<% regular_features.each do |key_array, val| %>
<% feature_key, display_name = key_array %>
<div class="flex items-center justify-between p-3 bg-white rounded-lg shadow-sm outline outline-1 outline-n-container">
<span class="text-sm text-slate-700"><%= display_name %></span>
<span><%= check_box "enabled_features", "feature_#{feature_key}", { checked: val, class: "h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-600" }, true, false %></span>
</div>
<% end %>
</div>
<hr class="my-8 boshadow-sm outline outline-1 outline-n-container">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<% premium_features.each do |key_array, val| %>
<% feature_key, display_name = key_array %>
<div class="flex items-center justify-between p-3 bg-white rounded-lg shadow-sm outline outline-1 outline-n-container">
<div class="flex items-center gap-2">
<span class="text-amber-500">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M480 224l-186.828 7.487L401.688 64l-59.247-32L256 208 169.824 32l-59.496 32 108.5 167.487L32 224v64l185.537-10.066L113.65 448l55.969 32L256 304l86.381 176 55.949-32-103.867-170.066L480 288z" fill="currentColor"/></svg>
</span>
<% end %>
<span><%= key %></span>
<% should_disable = is_premium && ChatwootHub.pricing_plan == 'community' %>
<span class='value-container'><%= check_box "enabled_features", "feature_#{key}", { checked: val, disabled: should_disable }, true, false %></span>
<span class="text-sm text-slate-700"><%= display_name %></span>
</div>
<% should_disable = ChatwootHub.pricing_plan == 'community' %>
<span><%= check_box "enabled_features", "feature_#{feature_key}", { checked: val, disabled: should_disable, class: "h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-600" }, true, false %></span>
</div>
<% end %>
</div>
</div>

View File

@@ -1,14 +1,34 @@
<div class='feature-container'>
<% SuperAdmin::AccountFeaturesHelper.filtered_features(field.data).each do |key, val| %>
<div class='feature-cell'>
<% if SuperAdmin::AccountFeaturesHelper.account_premium_features.include? key %>
<span class='icon-container'>
<svg class="inline" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><path d="M480 224l-186.828 7.487L401.688 64l-59.247-32L256 208 169.824 32l-59.496 32 108.5 167.487L32 224v64l185.537-10.066L113.65 448l55.969 32L256 304l86.381 176 55.949-32-103.867-170.066L480 288z" fill="currentColor"/></svg>
<div class="w-full">
<% regular_features, premium_features = SuperAdmin::AccountFeaturesHelper.partition_features(field.data) %>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<% regular_features.each do |key_array, val| %>
<% feature_key, display_name = key_array %>
<div class="flex items-center justify-between p-3 bg-white rounded-md outline outline-n-container outline-1 shadow-sm">
<span class="text-sm text-n-slate-12"><%= display_name %></span>
<span class="<%= val.present? ? 'bg-green-400 text-white': 'bg-slate-50 text-slate-800' %> rounded-full p-1 inline-flex right-4 top-5">
<svg width="12" height="12"><use xlink:href="#icon-tick-line" /></svg>
</span>
<% end %>
<span><%= key %></span>
<span class='value-container'><%= val.present? ? '✅' : '❌' %> </span>
</div>
<% end %>
</div>
<hr class="my-8 border-t border-n-weak">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<% premium_features.each do |key_array, val| %>
<% feature_key, display_name = key_array %>
<div class="flex items-center justify-between p-3 bg-white rounded-md outline outline-n-container outline-1 shadow-sm">
<div class="flex items-center gap-2">
<span class="bg-n-amber-3 text-n-amber-12 rounded-full p-1 inline-flex right-4 top-5">
<svg width="12" height="12"><use xlink:href="#icon-lock-line" /></svg>
</span>
<span class="text-sm text-n-slate-12"><%= display_name %></span>
</div>
<span class="<%= val.present? ? 'bg-green-400 text-white': 'bg-slate-50 text-slate-800' %> rounded-full p-1 inline-flex right-4 top-5">
<svg width="12" height="12"><use xlink:href="#icon-tick-line" /></svg>
</span>
</div>
<% end %>
</div>
</div>

View File

@@ -2,7 +2,7 @@
<hr/>
<%= form_for([:reset_cache, namespace, page.resource], method: :post, html: { class: "form" }) do |f| %>
<div class="form-actions">
<p>This will clear the IndexedDB cache keys from redis. <br>The next load will fetch the data from backend.</p>
<p class="pb-3">This will clear the IndexedDB cache keys from redis. <br>The next load will fetch the data from backend.</p>
<%= f.submit 'Reset Frontend Cache' %>
</div>
<% end %>

View File

@@ -4,7 +4,8 @@
<%= form_for([:seed, namespace, page.resource], method: :post, html: { class: "form" }) do |f| %>
<div class="form-actions">
<div><p> Click the button to generate seed data into this account for demos.</p>
<div class="pb-3">
<p>Click the button to generate seed data into this account for demos.</p>
<p class="text-color-red">Note: This will clear all the existing data in this account.</p>
</div>
<%= f.submit 'Generate Seed Data' %>

View File

@@ -13,10 +13,11 @@ as well as a link to its edit page.
as well as helpers for describing how each attribute of the resource
should be displayed.
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Show
%>
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Show %>
<% content_for(:title) { t("administrate.actions.show_resource", name: page.page_title) } %>
<% content_for(:title) do
t("administrate.actions.show_resource", name: page.page_title)
end %>
<header class="main-content__header" role="banner">
<h1 class="main-content__page-title">
@@ -24,48 +25,87 @@ as well as a link to its edit page.
</h1>
<div>
<%= link_to(
"Edit",
[:edit, namespace, page.resource],
class: "button",
) if accessible_action?(page.resource, :edit) %>
<%= if accessible_action?(page.resource, :edit)
link_to("Edit", [:edit, namespace, page.resource], class: "button")
end %>
</div>
</header>
<section class="main-content__body">
<dl>
<div class="pr-16">
<% page.attributes.each do |title, attributes| %>
<% if title.present? && title == 'all_features' %>
<% regular_features, premium_features =
attributes.partition do |attr|
!SuperAdmin::AccountFeaturesHelper.account_premium_features.include?(
attr.data.keys.first,
)
end %>
<div class="space-y-4">
<% regular_features.each do |attribute| %>
<div class="attribute-label" id="<%= attribute.name %>">
<%= t(
"helpers.label.#{resource_name}.#{attribute.name}",
default: page.resource.class.human_attribute_name(attribute.name),
) %>
</div>
<div class="attribute-data attribute-data--<%=attribute.html_class%>"><%= render_field attribute, page: page %></div>
<% end %>
</div>
<hr class="my-8 border-n-weak">
<div class="space-y-4">
<% premium_features.each do |attribute| %>
<div class="attribute-label" id="<%= attribute.name %>">
<%= t(
"helpers.label.#{resource_name}.#{attribute.name}",
default: page.resource.class.human_attribute_name(attribute.name),
) %>
</div>
<div class="attribute-data attribute-data--<%=attribute.html_class%>"><%= render_field attribute, page: page %></div>
<% end %>
</div>
<% else %>
<fieldset class="<%= "field-unit--nested" if title.present? %>">
<% if title.present? %>
<legend><%= t "helpers.label.#{page.resource_name}.#{title}", default: title %></legend>
<% end %>
<% attributes.each do |attribute| %>
<dt class="attribute-label" id="<%= attribute.name %>">
<div class="attribute-label" id="<%= attribute.name %>">
<%= t(
"helpers.label.#{resource_name}.#{attribute.name}",
default: page.resource.class.human_attribute_name(attribute.name),
) %>
</dt>
</div>
<dd class="attribute-data attribute-data--<%=attribute.html_class%>"
><%= render_field attribute, page: page %></dd>
<div class="attribute-data attribute-data--<%=attribute.html_class%>"><%= render_field attribute, page: page %></div>
<% end %>
</fieldset>
<% end %>
</dl>
<% end %>
</div>
</section>
<section class="main-content__body">
<% account_user_page = Administrate::Page::Form.new(AccountUserDashboard.new, AccountUser.new) %>
<% account_user_page =
Administrate::Page::Form.new(AccountUserDashboard.new, AccountUser.new) %>
<%= form_for([namespace, account_user_page.resource], html: { class: "form" }) do |f| %>
<% if account_user_page.resource.errors.any? %>
<div id="error_explanation">
<h2>
<%= t(
"administrate.form.errors",
pluralized_errors: pluralize(account_user_page.resource.errors.count, t("administrate.form.error")),
resource_name: display_resource_name(account_user_page.resource_name)
pluralized_errors:
pluralize(
account_user_page.resource.errors.count,
t("administrate.form.error"),
),
resource_name: display_resource_name(account_user_page.resource_name),
) %>
</h2>
@@ -80,9 +120,14 @@ as well as a link to its edit page.
<% account_user_page.attributes.each do |title, attributes| -%>
<% attributes.each do |attribute| %>
<% if attribute.name == "account" %>
<%= f.hidden_field('account_id', value: page.resource.id) %>
<%= f.hidden_field("account_id", value: page.resource.id) %>
<% else %>
<div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
<div
class="
field-unit field-unit--<%= attribute.html_class %>
field-unit--<%= requireness(attribute) %>
"
>
<%= render_field attribute, f: f %>
</div>
<% end %>

View File

@@ -1,19 +1,18 @@
<% content_for(:title) do %>
Settings
<% end %>
<header class="flex mx-8 py-4 items-center border-b border-solid border-slate-100" role="banner">
<div class="border border-solid border-slate-100 text-slate-700 mr-4 p-2 rounded-full">
<header class="flex px-8 py-4 items-center border-b border-n-weak" role="banner">
<div class="border border-n-weak mr-4 p-2 rounded-full">
<svg width="24" height="24"><use xlink:href="#icon-settings-2-line" /></svg>
</div>
<div class="flex flex-col h-14 justify-center">
<h1 class="text-base font-medium leading-6 text-slate-900" id="page-title">
<h1 class="text-base font-medium leading-6 text-n-slate-12" id="page-title">
<%= content_for(:title) %>
</h1>
<p class="text-sm font-normal leading-5 text-slate-500 m-0">Update your instance settings, access billing portal</p>
</div>
</header>
<section class="main-content__body">
<section class="main-content__body px-8">
<% if Redis::Alfred.get(Redis::Alfred::CHATWOOT_INSTALLATION_CONFIG_RESET_WARNING) %>
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-5" role="alert">
<strong class="font-bold">Alert!</strong>
@@ -22,28 +21,28 @@
</div>
<% end %>
<div class="bg-white py-2 px-3">
<div class="bg-white py-2 px-4 xl:px-0">
<div class="mb-4">
<div class="flex items-center gap-2">
<h2 class="h-5 leading-5 text-slate-900 font-medium">Current plan</h2>
<h2 class="h-5 leading-5 text-n-slate-12 font-medium">Current plan</h2>
<a href="<%= refresh_super_admin_settings_url %>" class="inline-flex gap-1 text-xs font-medium items-center text-woot-500 hover:text-woot-700">
<svg width="16" height="16"><use xlink:href="#icon-refresh-line" /></svg>
<span>Refresh</span>
</a>
</div>
<p class="text-slate-600 mt-1"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p>
<p class="text-n-slate-11 mt-1"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p>
<div class="flex items-center mt-6">
<h4 class="text-sm font-medium leading-5 h-5 text-slate-900 mr-4">Installation Identifier</h4>
<span class="text-sm leading-5 h-5 text-slate-600"><%= ChatwootHub.installation_identifier %></span>
<h4 class="text-sm font-medium leading-5 h-5 text-n-slate-12 mr-4">Installation Identifier</h4>
<span class="text-sm leading-5 h-5 text-n-slate-11"><%= ChatwootHub.installation_identifier %></span>
</div>
</div>
<div class="flex p-4 border border-solid border-slate-100 rounded-lg mt-8 items-start md:items-center shadow-sm flex-col md:flex-row">
<div class="flex p-4 outline outline-1 outline-n-container rounded-lg mt-8 items-start md:items-center shadow-sm flex-col md:flex-row">
<div class="flex flex-col flex-grow gap-1">
<h2 class="h-5 leading-5 text-slate-900 text-sm font-medium">Current plan</h2>
<p class="text-slate-600 m-0 text-sm"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p>
<h2 class="h-5 leading-5 text-n-slate-12 text-sm font-medium">Current plan</h2>
<p class="text-n-slate-11 m-0 text-sm"><%= SuperAdmin::FeaturesHelper.plan_details.html_safe %></p>
</div>
<a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer">
<button class="mt-4 md:mt-0 flex gap-1 items-center bg-transparent shadow-sm h-9 hover:bg-slate-100 hover:text-slate-800 border border-solid border-slate-100 rounded text-slate-700 font-medium p-2">
<button class="mt-4 md:mt-0 flex gap-1 items-center bg-transparent shadow-sm h-9 hover:text-n-slate-12 hover:bg-slate-50 outline outline-1 outline-n-container rounded text-n-slate-11 font-medium p-2">
<svg width="16" height="16"><use xlink:href="#icon-settings-2-line" /></svg>
<span class="px-1">Manage</span>
</button>
@@ -59,30 +58,30 @@
<% end %>
<div class="flex p-4 border border-solid border-slate-100 rounded-lg mt-4 items-start md:items-center shadow-sm flex-col md:flex-row">
<div class="flex p-4 outline outline-1 outline-n-container rounded-lg mt-4 items-start md:items-center shadow-sm flex-col md:flex-row">
<div class="flex flex-col flex-grow gap-1">
<h2 class="h-5 leading-5 text-slate-900 text-sm font-medium">Need help?</h2>
<p class="text-slate-600 m-0 text-sm">Do you face any issues? We are here to help.</p>
<h2 class="h-5 leading-5 text-n-slate-12 text-sm font-medium">Need help?</h2>
<p class="text-n-slate-11 m-0 text-sm">Do you face any issues? We are here to help.</p>
</div>
<a href="https://discord.gg/cJXdrwS" target="_blank">
<button class="flex mt-4 md:mt-0 gap-1 items-center bg-transparent shadow-sm h-9 bg-violet-500 hover:bg-violet-600 text-violet-100 hover:text-violet-200 border border-solid border-violet-600 rounded font-medium p-2">
<button class="flex mt-4 md:mt-0 gap-1 items-center bg-transparent shadow-sm h-9 bg-violet-500 hover:bg-violet-600 text-white border border-solid border-violet-600 rounded font-medium p-2">
<svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10a9.96 9.96 0 0 1-4.587-1.112l-3.826 1.067a1.25 1.25 0 0 1-1.54-1.54l1.068-3.823A9.96 9.96 0 0 1 2 12C2 6.477 6.477 2 12 2Zm0 1.5A8.5 8.5 0 0 0 3.5 12c0 1.47.373 2.883 1.073 4.137l.15.27-1.112 3.984 3.987-1.112.27.15A8.5 8.5 0 1 0 12 3.5ZM8.75 13h4.498a.75.75 0 0 1 .102 1.493l-.102.007H8.75a.75.75 0 0 1-.102-1.493L8.75 13h4.498H8.75Zm0-3.5h6.505a.75.75 0 0 1 .101 1.493l-.101.007H8.75a.75.75 0 0 1-.102-1.493L8.75 9.5h6.505H8.75Z" fill="currentColor"/></svg>
<span class="px-1">Community Support</span>
</button>
</a>
<% if ChatwootHub.pricing_plan !='community' %>
<button class="ml-4 flex gap-1 items-center bg-transparent h-9 hover:bg-slate-100 hover:text-slate-800 border border-solid border-slate-100 rounded text-slate-700 font-medium p-2" onclick="window.$chatwoot.toggle('open')">
<button class="ml-4 flex gap-1 items-center bg-transparent h-9 hover:text-n-slate-12 hover:bg-slate-50 border border-solid border-slate-100 rounded text-n-slate-11 font-medium p-2" onclick="window.$chatwoot.toggle('open')">
<svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10a9.96 9.96 0 0 1-4.587-1.112l-3.826 1.067a1.25 1.25 0 0 1-1.54-1.54l1.068-3.823A9.96 9.96 0 0 1 2 12C2 6.477 6.477 2 12 2Zm0 1.5A8.5 8.5 0 0 0 3.5 12c0 1.47.373 2.883 1.073 4.137l.15.27-1.112 3.984 3.987-1.112.27.15A8.5 8.5 0 1 0 12 3.5ZM8.75 13h4.498a.75.75 0 0 1 .102 1.493l-.102.007H8.75a.75.75 0 0 1-.102-1.493L8.75 13h4.498H8.75Zm0-3.5h6.505a.75.75 0 0 1 .101 1.493l-.101.007H8.75a.75.75 0 0 1-.102-1.493L8.75 9.5h6.505H8.75Z" fill="currentColor"/></svg>
<span class="px-1">Chat Support</span>
</button>
<% end %>
</div>
<div class="mt-10 py-4">
<h3 class="h-5 leading-5 text-slate-900 font-medium text-base">Features</h3>
<h3 class="h-5 leading-5 text-n-slate-12 font-medium text-base">Features</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-2 gap-y-3">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-2 gap-y-3">
<% SuperAdmin::FeaturesHelper.available_features.each do |feature, attrs| %>
<div class="group border-slate-100 border p-4 rounded-lg relative shadow-sm">
<div class="group outline outline-1 outline-n-container p-4 rounded-lg relative shadow-sm">
<span class="<%= attrs[:enabled] ? 'bg-green-400 text-white': 'bg-slate-300 text-slate-800' %> absolute rounded-full p-1 inline-flex right-4 top-5">
<svg width="14" height="14"><use xlink:href="<%= attrs[:enabled] ? '#icon-tick-line': '#icon-lock-line' %>" /></svg>
</span>
@@ -91,7 +90,7 @@
</div>
<% if !attrs[:enabled] %>
<div class="flex h-9 absolute top-5 items-center invisible group-hover:visible">
<a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer" class="flex gap-1 items-center bg-slate-100 h-9 hover:bg-slate-300 hover:text-slate-900 border border-solid border-slate-100 rounded text-slate-600 font-medium p-2">
<a href="<%= ChatwootHub.billing_url %>" target="_blank" rel="noopener noreferrer" class="flex gap-1 items-center bg-slate-100 h-9 hover:bg-slate-300 hover:text-n-slate-12 border border-solid border-slate-100 rounded text-n-slate-11 font-medium p-2">
<svg class="h-4 w-4" width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13.209 3.103c-.495-1.004-1.926-1.004-2.421 0L8.43 7.88l-5.273.766c-1.107.161-1.55 1.522-.748 2.303l3.815 3.72-.9 5.25c-.19 1.103.968 1.944 1.959 1.424l4.715-2.48 4.716 2.48c.99.52 2.148-.32 1.96-1.424l-.902-5.25 3.816-3.72c.8-.78.359-2.142-.748-2.303l-5.273-.766-2.358-4.777ZM9.74 8.615l2.258-4.576 2.259 4.576a1.35 1.35 0 0 0 1.016.738l5.05.734-3.654 3.562a1.35 1.35 0 0 0-.388 1.195l.862 5.03-4.516-2.375a1.35 1.35 0 0 0-1.257 0l-4.516 2.374.862-5.029a1.35 1.35 0 0 0-.388-1.195l-3.654-3.562 5.05-.734c.44-.063.82-.34 1.016-.738ZM1.164 3.782a.75.75 0 0 0 .118 1.054l2.5 2a.75.75 0 1 0 .937-1.172l-2.5-2a.75.75 0 0 0-1.055.118Z" fill="currentColor"/><path d="M22.836 18.218a.75.75 0 0 0-.117-1.054l-2.5-2a.75.75 0 0 0-.938 1.172l2.5 2a.75.75 0 0 0 1.055-.117ZM1.282 17.164a.75.75 0 1 0 .937 1.172l2.5-2a.75.75 0 0 0-.937-1.172l-2.5 2ZM22.836 3.782a.75.75 0 0 1-.117 1.054l-2.5 2a.75.75 0 0 1-.938-1.172l2.5-2a.75.75 0 0 1 1.055.118Z" fill="currentColor"/></svg>
<span class="px-1">Upgrade now</span>
</a>
@@ -99,7 +98,7 @@
<% end %>
<div class="flex items-center justify-between mb-1.5 mt-4">
<div class="flex items-center gap-2">
<h3 class="text-slate-900 font-medium"><%= attrs[:name] %></h3>
<h3 class="text-n-slate-12 font-medium"><%= attrs[:name] %></h3>
<% if attrs[:enterprise] %>
<span class="px-2 h-4 leading-4 rounded-xl text-green-800 font-medium bg-green-100/70 text-xxs">EE</span>
<% end %>
@@ -110,7 +109,7 @@
<% end %>
</div>
</div>
<p class="text-slate-600 mb-0"><%= attrs[:description] %></p>
<p class="text-n-slate-11 mb-0"><%= attrs[:description] %></p>
</div>
<% end %>
</div>

View File

@@ -1,107 +1,156 @@
# DO NOT change the order of features EVER
############################################
# name: the name to be used internally in the code
# display_name: the name to be used in the UI
# enabled: whether the feature is enabled by default
# help_url: the url to the help center article
# chatwoot_internal: whether the feature is internal to Chatwoot and should not be shown in the UI for other self hosted installations
# deprecated: purpose of feature flag is done, no need to show it in the UI anymore
- name: inbound_emails
display_name: Inbound Emails
enabled: true
- name: channel_email
display_name: Email Channel
enabled: true
help_url: https://chwt.app/hc/email
- name: channel_facebook
display_name: Facebook Channel
enabled: true
help_url: https://chwt.app/hc/fb
- name: channel_twitter
display_name: Twitter Channel
enabled: true
deprecated: true
- name: ip_lookup
display_name: IP Lookup
enabled: false
- name: disable_branding
display_name: Disable Branding
enabled: false
premium: true
- name: email_continuity_on_api_channel
display_name: Email Continuity on API Channel
enabled: false
- name: help_center
display_name: Help Center
enabled: true
help_url: https://chwt.app/hc/help-center
- name: agent_bots
display_name: Agent Bots
enabled: false
help_url: https://chwt.app/hc/agent-bots
- name: macros
display_name: Macros
enabled: true
- name: agent_management
display_name: Agent Management
enabled: true
- name: team_management
display_name: Team Management
enabled: true
help_url: https://chwt.app/hc/teams
- name: inbox_management
display_name: Inbox Management
enabled: true
- name: labels
display_name: Labels
enabled: true
help_url: https://chwt.app/hc/labels
- name: custom_attributes
display_name: Custom Attributes
enabled: true
help_url: https://chwt.app/hc/custom-attributes
- name: automations
display_name: Automations
enabled: true
- name: canned_responses
display_name: Canned Responses
enabled: true
help_url: https://chwt.app/hc/canned
- name: integrations
display_name: Integrations
enabled: true
help_url: https://chwt.app/hc/integrations
- name: voice_recorder
display_name: Voice Recorder
enabled: true
- name: mobile_v2
display_name: Mobile App V2
enabled: false
deprecated: true
- name: channel_website
display_name: Website Channel
enabled: true
- name: campaigns
display_name: Campaigns
enabled: true
help_url: https://chwt.app/hc/campaigns
- name: reports
display_name: Reports
enabled: true
help_url: https://chwt.app/hc/reports
- name: crm
display_name: CRM
enabled: true
- name: auto_resolve_conversations
display_name: Auto Resolve Conversations
enabled: true
- name: custom_reply_email
display_name: Custom Reply Email
enabled: false
- name: custom_reply_domain
display_name: Custom Reply Domain
enabled: false
- name: audit_logs
display_name: Audit Logs
enabled: false
premium: true
- name: response_bot
display_name: Response Bot
enabled: false
premium: true
chatwoot_internal: true
deprecated: true
- name: message_reply_to
display_name: Message Reply To
enabled: false
help_url: https://chwt.app/hc/reply-to
chatwoot_internal: true
deprecated: true
- name: insert_article_in_reply
display_name: Insert Article in Reply
enabled: false
chatwoot_internal: true
deprecated: true
- name: inbox_view
display_name: Inbox View
enabled: false
chatwoot_internal: true
- name: sla
display_name: SLA
enabled: false
premium: true
help_url: https://chwt.app/hc/sla
- name: help_center_embedding_search
display_name: Help Center Embedding Search
enabled: false
premium: true
chatwoot_internal: true
- name: linear_integration
display_name: Linear Integration
enabled: false
- name: captain_integration
display_name: Captain
enabled: false
premium: true
- name: custom_roles
display_name: Custom Roles
enabled: false
premium: true
- name: chatwoot_v4
display_name: Chatwoot V4
enabled: false
- name: report_v4
display_name: Report V4
enabled: false
- name: contact_chatwoot_support_team
display_name: Contact Chatwoot Support Team
enabled: true
chatwoot_internal: true