Compare commits

..
636 changed files with 4608 additions and 6173 deletions
-45
View File
@@ -1,45 +0,0 @@
## github action to check deployment success
## curl the deployment url and check for 200 status
## deployment url will be of the form chatwoot-pr-<pr_number>.herokuapp.com
name: Deploy Check
on:
pull_request:
jobs:
deployment_check:
name: Check Deployment
runs-on: ubuntu-latest
steps:
- name: Install jq
run: sudo apt-get install -y jq
- name: Print Deployment URL
run: echo "https://chatwoot-pr-${{ github.event.pull_request.number }}.herokuapp.com"
- name: Check Deployment Status
run: |
max_attempts=10
attempt=1
status_code=0
echo "Waiting for review app to be deployed/redeployed, trying in 10 minutes..."
sleep 600
while [ $attempt -le $max_attempts ]; do
response=$(curl -s -o /dev/null -w "%{http_code}" https://chatwoot-pr-${{ github.event.pull_request.number }}.herokuapp.com/api)
status_code=$(echo $response | head -n 1)
if [ $status_code -eq 200 ]; then
body=$(curl -s https://chatwoot-pr-${{ github.event.pull_request.number }}.herokuapp.com/api)
if echo "$body" | jq -e '.version and .timestamp and .queue_services == "ok" and .data_services == "ok"' > /dev/null; then
echo "Deployment successful"
exit 0
else
echo "Deployment status unknown, retrying in 3 minutes..."
sleep 180
fi
else
echo "Waiting for review app to be ready, retrying in 3 minutes..."
sleep 180
attempt=$((attempt + 1))
fi
done
echo "Deployment failed after $max_attempts attempts"
exit 1
fi
@@ -73,7 +73,6 @@ jobs:
spec/enterprise/controllers/api/v1/accounts/response_sources_controller_spec.rb \
spec/enterprise/services/enterprise/message_templates/response_bot_service_spec.rb \
spec/enterprise/controllers/enterprise/api/v1/accounts/inboxes_controller_spec.rb:47 \
spec/enterprise/jobs/enterprise/account/conversations_resolution_scheduler_job_spec.rb \
--profile=10 \
--format documentation
-12
View File
@@ -2,8 +2,6 @@ require:
- rubocop-performance
- rubocop-rails
- rubocop-rspec
- ./rubocop/use_from_email.rb
- ./rubocop/custom_cop_location.rb
Layout/LineLength:
Max: 150
@@ -142,16 +140,6 @@ RSpec/MultipleExpectations:
RSpec/MultipleMemoizedHelpers:
Max: 14
# custom rules
UseFromEmail:
Enabled: true
Exclude:
- 'app/models/user.rb'
- 'app/models/contact.rb'
CustomCopLocation:
Enabled: true
AllCops:
NewCops: enable
Exclude:
+2 -2
View File
@@ -3,8 +3,8 @@ source 'https://rubygems.org'
ruby '3.2.2'
##-- base gems for rails --##
gem 'rack-cors', '2.0.0', require: 'rack/cors'
gem 'rails', '~> 7.0.8.1'
gem 'rack-cors', require: 'rack/cors'
gem 'rails', '~> 7.0.8.0'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', require: false
+57 -57
View File
@@ -33,70 +33,70 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.8.1)
actionpack (= 7.0.8.1)
activesupport (= 7.0.8.1)
actioncable (7.0.8)
actionpack (= 7.0.8)
activesupport (= 7.0.8)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.8.1)
actionpack (= 7.0.8.1)
activejob (= 7.0.8.1)
activerecord (= 7.0.8.1)
activestorage (= 7.0.8.1)
activesupport (= 7.0.8.1)
actionmailbox (7.0.8)
actionpack (= 7.0.8)
activejob (= 7.0.8)
activerecord (= 7.0.8)
activestorage (= 7.0.8)
activesupport (= 7.0.8)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.8.1)
actionpack (= 7.0.8.1)
actionview (= 7.0.8.1)
activejob (= 7.0.8.1)
activesupport (= 7.0.8.1)
actionmailer (7.0.8)
actionpack (= 7.0.8)
actionview (= 7.0.8)
activejob (= 7.0.8)
activesupport (= 7.0.8)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.8.1)
actionview (= 7.0.8.1)
activesupport (= 7.0.8.1)
actionpack (7.0.8)
actionview (= 7.0.8)
activesupport (= 7.0.8)
rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.8.1)
actionpack (= 7.0.8.1)
activerecord (= 7.0.8.1)
activestorage (= 7.0.8.1)
activesupport (= 7.0.8.1)
actiontext (7.0.8)
actionpack (= 7.0.8)
activerecord (= 7.0.8)
activestorage (= 7.0.8)
activesupport (= 7.0.8)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.8.1)
activesupport (= 7.0.8.1)
actionview (7.0.8)
activesupport (= 7.0.8)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_record_query_trace (1.8)
activejob (7.0.8.1)
activesupport (= 7.0.8.1)
activejob (7.0.8)
activesupport (= 7.0.8)
globalid (>= 0.3.6)
activemodel (7.0.8.1)
activesupport (= 7.0.8.1)
activerecord (7.0.8.1)
activemodel (= 7.0.8.1)
activesupport (= 7.0.8.1)
activemodel (7.0.8)
activesupport (= 7.0.8)
activerecord (7.0.8)
activemodel (= 7.0.8)
activesupport (= 7.0.8)
activerecord-import (1.4.1)
activerecord (>= 4.2)
activestorage (7.0.8.1)
actionpack (= 7.0.8.1)
activejob (= 7.0.8.1)
activerecord (= 7.0.8.1)
activesupport (= 7.0.8.1)
activestorage (7.0.8)
actionpack (= 7.0.8)
activejob (= 7.0.8)
activerecord (= 7.0.8)
activesupport (= 7.0.8)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.8.1)
activesupport (7.0.8)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -559,12 +559,12 @@ GEM
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.7.3)
rack (2.2.8.1)
rack (2.2.8)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-contrib (2.4.0)
rack (< 4)
rack-cors (2.0.0)
rack-cors (2.0.1)
rack (>= 2.0.0)
rack-mini-profiler (3.2.0)
rack (>= 1.2.0)
@@ -575,20 +575,20 @@ GEM
rack-test (2.1.0)
rack (>= 1.3)
rack-timeout (0.6.3)
rails (7.0.8.1)
actioncable (= 7.0.8.1)
actionmailbox (= 7.0.8.1)
actionmailer (= 7.0.8.1)
actionpack (= 7.0.8.1)
actiontext (= 7.0.8.1)
actionview (= 7.0.8.1)
activejob (= 7.0.8.1)
activemodel (= 7.0.8.1)
activerecord (= 7.0.8.1)
activestorage (= 7.0.8.1)
activesupport (= 7.0.8.1)
rails (7.0.8)
actioncable (= 7.0.8)
actionmailbox (= 7.0.8)
actionmailer (= 7.0.8)
actionpack (= 7.0.8)
actiontext (= 7.0.8)
actionview (= 7.0.8)
activejob (= 7.0.8)
activemodel (= 7.0.8)
activerecord (= 7.0.8)
activestorage (= 7.0.8)
activesupport (= 7.0.8)
bundler (>= 1.15.0)
railties (= 7.0.8.1)
railties (= 7.0.8)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
@@ -596,9 +596,9 @@ GEM
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.0.8.1)
actionpack (= 7.0.8.1)
activesupport (= 7.0.8.1)
railties (7.0.8)
actionpack (= 7.0.8)
activesupport (= 7.0.8)
method_source
rake (>= 12.2)
thor (~> 1.0)
@@ -918,10 +918,10 @@ DEPENDENCIES
puma
pundit
rack-attack (>= 6.7.0)
rack-cors (= 2.0.0)
rack-cors
rack-mini-profiler (>= 3.2.0)
rack-timeout
rails (~> 7.0.8.1)
rails (~> 7.0.8.0)
redis
redis-namespace
responders (>= 3.1.1)
+1
View File
@@ -23,6 +23,7 @@ Customer engagement suite, an open-source alternative to Intercom, Zendesk, Sale
<img src="https://img.shields.io/github/commit-activity/m/chatwoot/chatwoot" alt="Commits-per-month">
<a title="Crowdin" target="_self" href="https://chatwoot.crowdin.com/chatwoot"><img src="https://badges.crowdin.net/e/37ced7eba411064bd792feb3b7a28b16/localized.svg"></a>
<a href="https://discord.gg/cJXdrwS"><img src="https://img.shields.io/discord/647412545203994635" alt="Discord"></a>
<a href="https://huntr.dev/bounties/disclose"><img src="https://cdn.huntr.dev/huntr_security_badge_mono.svg" alt="Huntr"></a>
<a href="https://status.chatwoot.com"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchatwoot%2Fstatus%2Fmaster%2Fapi%2Fchatwoot%2Fuptime.json" alt="uptime"></a>
<a href="https://status.chatwoot.com"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fchatwoot%2Fstatus%2Fmaster%2Fapi%2Fchatwoot%2Fresponse-time.json" alt="response time"></a>
<a href="https://artifacthub.io/packages/helm/chatwoot/chatwoot"><img src="https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/artifact-hub" alt="Artifact HUB"></a>
-5
View File
@@ -72,11 +72,6 @@
"scripts": {
"test": "bundle exec rake test"
}
},
"review": {
"scripts": {
"postdeploy": "bundle exec rails db:seed"
}
}
}
}
+1 -1
View File
@@ -59,7 +59,7 @@ class ContactIdentifyAction
def existing_email_contact
return if params[:email].blank?
@existing_email_contact ||= account.contacts.from_email(params[:email])
@existing_email_contact ||= account.contacts.find_by(email: params[:email])
end
def existing_phone_number_contact
+1 -1
View File
@@ -27,7 +27,7 @@ class AgentBuilder
# Finds a user by email or creates a new one with a temporary password.
# @return [User] the found or created user.
def find_or_create_user
user = User.from_email(email)
user = User.find_by(email: email)
return user if user
temp_password = "1!aA#{SecureRandom.alphanumeric(12)}"
@@ -75,7 +75,7 @@ class ContactInboxWithContactBuilder
def find_contact_by_email(email)
return if email.blank?
account.contacts.from_email(email)
account.contacts.find_by(email: email.downcase)
end
def find_contact_by_phone_number(phone_number)
-10
View File
@@ -54,13 +54,6 @@ class V2::ReportBuilder
}
end
def bot_summary
{
bot_resolutions_count: bot_resolutions.count,
bot_handoffs_count: bot_handoffs.count
}
end
def conversation_metrics
if params[:type].equal?(:account)
live_conversations
@@ -78,8 +71,6 @@ class V2::ReportBuilder
avg_first_response_time
avg_resolution_time reply_time
resolutions_count
bot_resolutions_count
bot_handoffs_count
reply_time].include?(params[:metric])
end
@@ -132,7 +123,6 @@ class V2::ReportBuilder
unattended: @open_conversations.unattended.count
}
metric[:unassigned] = @open_conversations.unassigned.count if params[:type].equal?(:account)
metric[:pending] = @open_conversations.pending.count if params[:type].equal?(:account)
metric
end
end
@@ -1,54 +0,0 @@
class V2::Reports::BotMetricsBuilder
include DateRangeHelper
attr_reader :account, :params
def initialize(account, params)
@account = account
@params = params
end
def metrics
{
conversation_count: bot_conversations.count,
message_count: bot_messages.count,
resolution_rate: bot_resolution_rate.to_i,
handoff_rate: bot_handoff_rate.to_i
}
end
private
def bot_activated_inbox_ids
@bot_activated_inbox_ids ||= account.inboxes.filter(&:active_bot?).map(&:id)
end
def bot_conversations
@bot_conversations ||= account.conversations.where(inbox_id: bot_activated_inbox_ids).where(created_at: range)
end
def bot_messages
@bot_messages ||= account.messages.outgoing.where(conversation_id: bot_conversations.ids).where(created_at: range)
end
def bot_resolutions_count
account.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_resolved,
created_at: range).distinct.count
end
def bot_handoffs_count
account.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_handoff,
created_at: range).distinct.count
end
def bot_resolution_rate
return 0 if bot_conversations.count.zero?
bot_resolutions_count.to_f / bot_conversations.count * 100
end
def bot_handoff_rate
return 0 if bot_conversations.count.zero?
bot_handoffs_count.to_f / bot_conversations.count * 100
end
end
@@ -36,10 +36,6 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
end
end
def update
@conversation.update!(permitted_update_params)
end
def filter
result = ::Conversations::FilterService.new(params.permit!, current_user).perform
@conversations = result[:conversations]
@@ -114,11 +110,6 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
private
def permitted_update_params
# TODO: Move the other conversation attributes to this method and remove specific endpoints for each attribute
params.permit(:priority)
end
def update_last_seen_on_conversation(last_seen_at, update_assignee)
# rubocop:disable Rails/SkipsModelValidations
@conversation.update_column(:agent_last_seen_at, last_seen_at)
@@ -185,5 +176,3 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
@conversation.assignee_id? && Current.user == @conversation.assignee
end
end
Api::V1::Accounts::ConversationsController.prepend_mod_with('Api::V1::Accounts::ConversationsController')
@@ -14,12 +14,6 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
render json: summary_metrics
end
def bot_summary
summary = V2::ReportBuilder.new(Current.account, current_summary_params).bot_summary
summary[:previous] = V2::ReportBuilder.new(Current.account, previous_summary_params).bot_summary
render json: summary
end
def agents
@report_data = generate_agents_report
generate_csv('agents_report', 'api/v2/accounts/reports/agents')
@@ -54,11 +48,6 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
render json: conversation_metrics
end
def bot_metrics
bot_metrics = V2::Reports::BotMetricsBuilder.new(Current.account, params).metrics
render json: bot_metrics
end
private
def generate_csv(filename, template)
@@ -1,6 +1,6 @@
module AccessTokenAuthHelper
BOT_ACCESSIBLE_ENDPOINTS = {
'api/v1/accounts/conversations' => %w[toggle_status toggle_priority create update],
'api/v1/accounts/conversations' => %w[toggle_status toggle_priority create],
'api/v1/accounts/conversations/messages' => ['create'],
'api/v1/accounts/conversations/assignments' => ['create']
}.freeze
@@ -5,7 +5,7 @@ class DeviseOverrides::PasswordsController < Devise::PasswordsController
skip_before_action :authenticate_user!, raise: false
def create
@user = User.from_email(params[:email])
@user = User.find_by(email: params[:email])
if @user
@user.send_reset_password_instructions
build_response(I18n.t('messages.reset_password_success'), 200)
@@ -33,7 +33,7 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
def process_sso_auth_token
return if params[:email].blank?
user = User.from_email(params[:email])
user = User.find_by(email: params[:email])
@resource = user if user&.valid_sso_auth_token?(params[:sso_auth_token])
end
end
@@ -8,7 +8,7 @@ class Platform::Api::V1::UsersController < PlatformController
def show; end
def create
@resource = (User.from_email(user_params[:email]) || User.new(user_params))
@resource = (User.find_by(email: user_params[:email]) || User.new(user_params))
@resource.skip_confirmation!
@resource.save!
@platform_app.platform_app_permissibles.find_or_create_by!(permissible: @resource)
@@ -5,10 +5,6 @@
# If you want to add pagination or other controller-level concerns,
# you're free to overwrite the RESTful controller actions.
class SuperAdmin::ApplicationController < Administrate::ApplicationController
include ActionView::Helpers::TagHelper
include ActionView::Context
helper_method :render_vue_component
# authenticiation done via devise : SuperAdmin Model
before_action :authenticate_super_admin!
@@ -27,17 +23,6 @@ class SuperAdmin::ApplicationController < Administrate::ApplicationController
private
def render_vue_component(component_name, props = {})
html_options = {
id: 'app',
data: {
component_name: component_name,
props: props.to_json
}
}
content_tag(:div, '', html_options)
end
def invalid_action_perfomed
# rubocop:disable Rails/I18nLocaleTexts
flash[:error] = 'Invalid action performed'
-83
View File
@@ -1,83 +0,0 @@
module ContactHelper
def parse_name(full_name)
# If the input is nil or not a string, return a hash with all values set to nil
return default_name_hash if invalid_name?(full_name)
# If the input is a number, return a hash with the number as the first name
return numeric_name_hash(full_name) if valid_number?(full_name)
full_name = full_name.squish
# If full name consists of only one word, consider it as the first name
return single_word_name_hash(full_name) if single_word?(full_name)
parts = split_name(full_name)
parts = handle_conjunction(parts)
build_name_hash(parts)
end
private
def default_name_hash
{ first_name: nil, last_name: nil, middle_name: nil, prefix: nil, suffix: nil }
end
def invalid_name?(full_name)
!full_name.is_a?(String) || full_name.empty?
end
def numeric_name_hash(full_name)
{ first_name: full_name, last_name: nil, middle_name: nil, prefix: nil, suffix: nil }
end
def valid_number?(full_name)
full_name.gsub(/\s+/, '').match?(/\A\+?\d+\z/)
end
def single_word_name_hash(full_name)
{ first_name: full_name, last_name: nil, middle_name: nil, prefix: nil, suffix: nil }
end
def single_word?(full_name)
full_name.split.size == 1
end
def split_name(full_name)
full_name.split
end
def handle_conjunction(parts)
conjunctions = ['and', '&']
parts.each_index do |i|
next unless conjunctions.include?(parts[i]) && i.positive?
parts[i - 1] = [parts[i - 1], parts[i + 1]].join(' ')
parts.delete_at(i)
parts.delete_at(i)
end
parts
end
def build_name_hash(parts)
suffix = parts.pop if parts.last.match?(/(\w+\.|[IVXLM]+|[A-Z]+)$/)
last_name = parts.pop
prefix = parts.shift if parts.first.match?(/^\w+\./)
first_name = parts.shift
middle_name = parts.join(' ')
hash = {
first_name: first_name,
last_name: last_name,
prefix: prefix,
middle_name: middle_name,
suffix: suffix
}
# Reverse name if "," was used in Last, First notation.
if hash[:first_name] =~ /,$/
hash[:first_name] = hash[:last_name]
hash[:last_name] = Regexp.last_match.pre_match
end
hash
end
end
-18
View File
@@ -32,14 +32,6 @@ module ReportHelper
(get_grouped_values resolutions).count
end
def bot_resolutions_count
(get_grouped_values bot_resolutions).count
end
def bot_handoffs_count
(get_grouped_values bot_handoffs).count
end
def conversations
scope.conversations.where(account_id: account.id, created_at: range)
end
@@ -57,16 +49,6 @@ module ReportHelper
conversations: { status: :resolved }, created_at: range).distinct
end
def bot_resolutions
scope.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_resolved,
conversations: { status: :resolved }, created_at: range).distinct
end
def bot_handoffs
scope.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_handoff,
created_at: range).distinct
end
def avg_first_response_time
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'first_response', account_id: account.id))
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
-18
View File
@@ -84,24 +84,6 @@ class ReportsAPI extends ApiClient {
params: { since, until, business_hours: businessHours },
});
}
getBotMetrics({ from, to } = {}) {
return axios.get(`${this.url}/bot_metrics`, {
params: { since: from, until: to },
});
}
getBotSummary({ from, to, groupBy, businessHours } = {}) {
return axios.get(`${this.url}/bot_summary`, {
params: {
since: from,
until: to,
type: 'account',
group_by: groupBy,
business_hours: businessHours,
},
});
}
}
export default new ReportsAPI();
@@ -111,40 +111,6 @@ describe('#Reports API', () => {
});
});
it('#getBotMetrics', () => {
reportsAPI.getBotMetrics({ from: 1621103400, to: 1621621800 });
expect(axiosMock.get).toHaveBeenCalledWith(
'/api/v2/reports/bot_metrics',
{
params: {
since: 1621103400,
until: 1621621800,
},
}
);
});
it('#getBotSummary', () => {
reportsAPI.getBotSummary({
from: 1621103400,
to: 1621621800,
groupBy: 'date',
businessHours: true,
});
expect(axiosMock.get).toHaveBeenCalledWith(
'/api/v2/reports/bot_summary',
{
params: {
since: 1621103400,
until: 1621621800,
type: 'account',
group_by: 'date',
business_hours: true,
},
}
);
});
it('#getConversationMetric', () => {
reportsAPI.getConversationMetric('account');
expect(axiosMock.get).toHaveBeenCalledWith(
@@ -0,0 +1,58 @@
.button {
font-family: $body-font-family;
font-weight: $font-weight-medium;
&.round {
border-radius: 1000px;
}
}
select {
height: 2.5rem;
}
.card {
margin-bottom: var(--space-small);
padding: var(--space-normal);
}
code {
border: 0;
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
'"Liberation Mono"', '"Courier New"', 'monospace';
font-size: $font-size-mini;
&.hljs {
background: $color-background;
border-radius: var(--border-radius-large);
padding: $space-two;
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100;
}
}
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-capitalize {
text-transform: capitalize;
}
.cursor-pointer {
cursor: pointer;
}
// remove when grid gutters are fixed
.columns.with-right-space {
padding-right: var(--space-normal);
}
.badge {
border-radius: var(--border-radius-normal);
}
.padding-right-small {
padding-right: var(--space-one);
}
@@ -0,0 +1,623 @@
// Foundation for Sites Settings
// -----------------------------
//
// Table of Contents:
//
// 1. Global
// 2. Breakpoints
// 3. The Grid
// 4. Base Typography
// 5. Typography Helpers
// 6. Abide
// 7. Accordion
// 8. Accordion Menu
// 9. Badge
// 10. Breadcrumbs
// 11. Button
// 12. Button Group
// 13. Callout
// 14. Card
// 15. Close Button
// 16. Drilldown
// 17. Dropdown
// 18. Dropdown Menu
// 19. Forms
// 20. Label
// 21. Media Object
// 22. Menu
// 23. Meter
// 24. Off-canvas
// 25. Orbit
// 26. Pagination
// 27. Progress Bar
// 28. Responsive Embed
// 29. Reveal
// 30. Slider
// 31. Switch
// 32. Table
// 33. Tabs
// 34. Thumbnail
// 35. Title Bar
// 36. Tooltip
// 37. Top Bar
@import '~foundation-sites/scss/util/util';
// 1. Global
// ---------
// Disable contrast warnings in Foundation.
$contrast-warnings: false;
$global-font-size: 16px;
$global-width: 100%;
$global-lineheight: 1.5;
$foundation-palette: (primary: $color-woot,
secondary: #5d7592,
success: #44ce4b,
warning: #ffc532,
alert: #ff382d);
$light-gray: #c0ccda;
$medium-gray: #8492a6;
$dark-gray: $color-gray;
$black: #000;
$white: #fff;
$body-background: $white;
$body-font-color: $color-body;
$body-font-family: 'PlusJakarta',
-apple-system,
system-ui,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Tahoma,
Arial,
sans-serif;
$body-antialiased: true;
$global-margin: $space-small;
$global-padding: $space-small;
$global-weight-normal: normal;
$global-weight-bold: bold;
$global-radius: 0;
$global-text-direction: ltr;
$global-flexbox: false;
$print-transparent-backgrounds: true;
@include add-foundation-colors;
// 2. Breakpoints
// --------------
$breakpoints: (small: 0,
medium: 640px,
large: 1024px,
xlarge: 1200px,
xxlarge: 1400px,
xxxlarge: 1600px,
);
$print-breakpoint: large;
$breakpoint-classes: (small medium large);
// 3. The Grid
// -----------
$grid-row-width: $global-width;
$grid-column-count: 12;
$grid-column-gutter: (small: $zero,
medium: $zero);
$grid-column-align-edge: true;
$block-grid-max: 8;
// 4. Base Typography
// ------------------
$header-font-family: $body-font-family;
$header-font-weight: $font-weight-medium;
$header-font-style: normal;
$font-family-monospace: $body-font-family;
$header-color: $color-heading;
$header-lineheight: 1.4;
$header-margin-bottom: 0.3125rem;
$header-styles: (small: ("h1": ("font-size": 24),
"h2": ("font-size": 20),
"h3": ("font-size": 19),
"h4": ("font-size": 18),
"h5": ("font-size": 17),
"h6": ("font-size": 16)),
medium: ("h1": ("font-size": 48),
"h2": ("font-size": 40),
"h3": ("font-size": 31),
"h4": ("font-size": 25),
"h5": ("font-size": 20),
"h6": ("font-size": 16)));
$header-text-rendering: optimizeLegibility;
$small-font-size: 80%;
$header-small-font-color: $medium-gray;
$paragraph-lineheight: 1.65;
$paragraph-margin-bottom: var(--space-small);
$paragraph-text-rendering: optimizeLegibility;
$code-color: $black;
$code-font-family: $font-family-monospace;
$code-font-weight: $global-weight-normal;
$code-background: $light-gray;
$code-border: 1px solid $medium-gray;
$code-padding: rem-calc(2 5 1);
$anchor-color: $primary-color;
$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
$anchor-text-decoration: none;
$anchor-text-decoration-hover: none;
$hr-width: $global-width;
$hr-border: 1px solid $medium-gray;
$hr-margin: rem-calc(20) auto;
$list-lineheight: $paragraph-lineheight;
$list-margin-bottom: $paragraph-margin-bottom;
$list-style-type: disc;
$list-style-position: outside;
$list-side-margin: 0.78125rem;
$list-nested-side-margin: 0.78125rem;
$defnlist-margin-bottom: 0.6875rem;
$defnlist-term-weight: $global-weight-bold;
$defnlist-term-margin-bottom: 0.1875rem;
$blockquote-color: $dark-gray;
$blockquote-padding: rem-calc(9 20 0 19);
$blockquote-border: 1px solid $medium-gray;
$cite-font-size: rem-calc(13);
$cite-color: $dark-gray;
$cite-pseudo-content: '\2014 \0020';
$keystroke-font: $font-family-monospace;
$keystroke-color: $black;
$keystroke-background: $light-gray;
$keystroke-padding: rem-calc(2 4 0);
$keystroke-radius: $global-radius;
$abbr-underline: 1px dotted $black;
// 5. Typography Helpers
// ---------------------
$lead-font-size: $global-font-size * 1.25;
$lead-lineheight: 1.6;
$subheader-lineheight: 1.4;
$subheader-color: $dark-gray;
$subheader-font-weight: $global-weight-normal;
$subheader-margin-top: 0.125rem;
$subheader-margin-bottom: 0.3125rem;
$stat-font-size: 1.5625rem;
// 6. Abide
// --------
$abide-inputs: true;
$abide-labels: true;
$input-background-invalid: get-color(alert);
$form-label-color-invalid: get-color(alert);
$input-error-color: get-color(alert);
$input-error-font-size: rem-calc(12);
$input-error-font-weight: $global-weight-bold;
// 7. Accordion
// ------------
$accordion-background: $white;
$accordion-plusminus: true;
$accordion-title-font-size: rem-calc(12);
$accordion-item-color: $primary-color;
$accordion-item-background-hover: $light-gray;
$accordion-item-padding: 0.78125rem 0.625rem;
$accordion-content-background: $white;
$accordion-content-border: 1px solid $light-gray;
$accordion-content-color: $body-font-color;
$accordion-content-padding: 0.625rem;
// 8. Accordion Menu
// -----------------
$accordionmenu-arrows: true;
$accordionmenu-arrow-color: $primary-color;
$accordionmenu-arrow-size: 6px;
// 9. Badge
// --------
$badge-background: $primary-color;
$badge-color: $white;
$badge-color-alt: $black;
$badge-palette: $foundation-palette;
$badge-padding: var(--space-smaller);
$badge-minwidth: 2.1em;
$badge-font-size: var(--font-size-nano);
// 10. Breadcrumbs
// ---------------
$breadcrumbs-margin: 0 0 $global-margin 0;
$breadcrumbs-item-font-size: rem-calc(11);
$breadcrumbs-item-color: $primary-color;
$breadcrumbs-item-color-current: $black;
$breadcrumbs-item-color-disabled: $medium-gray;
$breadcrumbs-item-margin: 0.46875rem;
$breadcrumbs-item-uppercase: true;
$breadcrumbs-item-slash: true;
// 11. Button
// ----------
$button-padding: var(--space-smaller) 1em;
$button-margin: 0 0 $global-margin 0;
$button-fill: solid;
$button-background: $primary-color;
$button-background-hover: scale-color($button-background, $lightness: -15%);
$button-color: $white;
$button-color-alt: $white;
$button-radius: var(--border-radius-normal);
$button-sizes: (tiny: var(--font-size-micro),
small: var(--font-size-mini),
default: var(--font-size-small),
large: var(--font-size-medium));
$button-palette: $foundation-palette;
$button-opacity-disabled: 0.4;
$button-background-hover-lightness: -20%;
$button-hollow-hover-lightness: -50%;
$button-transition: background-color 0.25s ease-out,
color 0.25s ease-out;
// 12. Button Group
// ----------------
$buttongroup-margin: 0;
$buttongroup-spacing: 0;
$buttongroup-child-selector: '.button';
$buttongroup-expand-max: 6;
$buttongroup-radius-on-each: false;
// 13. Callout
// -----------
$callout-background: $white;
$callout-background-fade: 85%;
$callout-border: 1px solid rgba($black, 0.25);
$callout-margin: 0 0 0.625rem 0;
$callout-padding: 0.625rem;
$callout-font-color: $body-font-color;
$callout-font-color-alt: $body-background;
$callout-radius: $global-radius;
$callout-link-tint: 30%;
// 14. Card
// --------
$card-background: $white;
$card-font-color: $body-font-color;
$card-divider-background: $light-gray;
$card-border: 1px solid var(--color-border);
$card-shadow: var(--shadow-small);
$card-border-radius: var(--border-radius-normal);
$card-padding: var(--space-small);
$card-margin: $global-margin;
// 15. Close Button
// ----------------
$closebutton-position: right top;
$closebutton-offset-horizontal: (small: 0.66rem,
medium: 1rem);
$closebutton-offset-vertical: (small: 0.33em,
medium: 0.5rem);
$closebutton-size: (small: 1.5em,
medium: 2em);
$closebutton-lineheight: 1;
$closebutton-color: $dark-gray;
$closebutton-color-hover: $black;
// 16. Drilldown
// -------------
$drilldown-transition: transform 0.15s linear;
$drilldown-arrows: true;
$drilldown-arrow-color: $primary-color;
$drilldown-arrow-size: 6px;
$drilldown-background: $white;
// 17. Dropdown
// ------------
$dropdown-padding: 0.625rem;
$dropdown-background: $body-background;
$dropdown-border: 1px solid $medium-gray;
$dropdown-font-size: 0.625rem;
$dropdown-width: 300px;
$dropdown-radius: $global-radius;
$dropdown-sizes: (tiny: 100px,
small: 200px,
large: 400px);
// 18. Dropdown Menu
// -----------------
$dropdownmenu-arrows: true;
$dropdownmenu-arrow-color: $anchor-color;
$dropdownmenu-arrow-size: 6px;
$dropdownmenu-min-width: 200px;
$dropdownmenu-background: $white;
$dropdownmenu-border: 1px solid $medium-gray;
// 19. Forms
// ---------
$fieldset-border: 1px solid $light-gray;
$fieldset-padding: $space-two;
$fieldset-margin: $space-one $zero;
$legend-padding: rem-calc(0 3);
$form-spacing: $space-normal;
$helptext-color: $color-body;
$helptext-font-size: $font-size-small;
$helptext-font-style: italic;
$input-prefix-color: $color-body;
$input-prefix-background: var(--b-100);
$input-prefix-border: 1px solid $color-border;
$input-prefix-padding: 0.625rem;
$form-label-color: $color-body;
$form-label-font-size: rem-calc(14);
$form-label-font-weight: $font-weight-medium;
$form-label-line-height: 1.8;
$select-background: $white;
$select-triangle-color: $dark-gray;
$select-radius: var(--border-radius-normal);
$input-color: $color-body;
$input-placeholder-color: $light-gray;
$input-font-family: inherit;
$input-font-size: $font-size-default;
$input-font-weight: $global-weight-normal;
$input-background: $white;
$input-background-focus: $white;
$input-background-disabled: $light-gray;
$input-border: 1px solid var(--s-200);
$input-border-focus: 1px solid lighten($primary-color, 15%);
$input-shadow: 0;
$input-shadow-focus: 0;
$input-cursor-disabled: not-allowed;
$input-transition: border-color 0.25s ease-in-out;
$input-number-spinners: true;
$input-radius: var(--border-radius-normal);
$form-button-radius: var(--border-radius-normal);
// 20. Label
// ---------
$label-background: $white;
$label-color: $black;
$label-color-alt: $black;
$label-palette: $foundation-palette;
$label-font-size: $font-size-mini;
$label-padding: $space-smaller $space-small;
$label-radius: var(--border-radius-small);
// 21. Media Object
// ----------------
$mediaobject-margin-bottom: $global-margin;
$mediaobject-section-padding: $global-padding;
$mediaobject-image-width-stacked: 100%;
// 22. Menu
// --------
$menu-margin: 0;
$menu-margin-nested: $space-medium;
$menu-item-padding: $space-slab;
$menu-item-color-active: $white;
$menu-item-background-active: $color-background;
$menu-icon-spacing: 0.15625rem;
$menu-item-background-hover: $light-gray;
$menu-border: $light-gray;
// 23. Meter
// ---------
$meter-height: 0.625rem;
$meter-radius: $global-radius;
$meter-background: $medium-gray;
$meter-fill-good: $success-color;
$meter-fill-medium: $warning-color;
$meter-fill-bad: $alert-color;
// 24. Off-canvas
// --------------
$offcanvas-sizes: (small: 14.375,
medium: 14.375,
);
$offcanvas-vertical-sizes: (small: 14.375,
medium: 14.375,
);
$offcanvas-background: $light-gray;
$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
$offcanvas-push-zindex: 1;
$offcanvas-overlap-zindex: 10;
$offcanvas-reveal-zindex: 1;
$offcanvas-transition-length: 0.5s;
$offcanvas-transition-timing: ease;
$offcanvas-fixed-reveal: true;
$offcanvas-exit-background: rgba($white, 0.25);
$maincontent-class: 'off-canvas-content';
// 25. Orbit
// ---------
$orbit-bullet-background: $medium-gray;
$orbit-bullet-background-active: $dark-gray;
$orbit-bullet-diameter: 0.75rem;
$orbit-bullet-margin: 0.0625rem;
$orbit-bullet-margin-top: 0.5rem;
$orbit-bullet-margin-bottom: 0.5rem;
$orbit-caption-background: rgba($black, 0.5);
$orbit-caption-padding: 0.625rem;
$orbit-control-background-hover: rgba($black, 0.5);
$orbit-control-padding: 0.625rem;
$orbit-control-zindex: 10;
// 26. Pagination
// --------------
$pagination-font-size: rem-calc(14);
$pagination-margin-bottom: $global-margin;
$pagination-item-color: $black;
$pagination-item-padding: rem-calc(3 10);
$pagination-item-spacing: rem-calc(1);
$pagination-radius: $global-radius;
$pagination-item-background-hover: $light-gray;
$pagination-item-background-current: $primary-color;
$pagination-item-color-current: $white;
$pagination-item-color-disabled: $medium-gray;
$pagination-ellipsis-color: $black;
$pagination-mobile-items: false;
$pagination-mobile-current-item: false;
$pagination-arrows: true;
// 27. Progress Bar
// ----------------
$progress-height: 0.625rem;
$progress-background: $medium-gray;
$progress-margin-bottom: $global-margin;
$progress-meter-background: $primary-color;
$progress-radius: $global-radius;
// 28. Responsive Embed
// --------------------
$responsive-embed-margin-bottom: rem-calc(16);
$responsive-embed-ratios: (default: 4 by 3,
widescreen: 16 by 9);
// 29. Reveal
// ----------
$reveal-background: $white;
$reveal-width: 600px;
$reveal-max-width: $global-width;
$reveal-padding: $global-padding;
$reveal-border: 1px solid $medium-gray;
$reveal-radius: $global-radius;
$reveal-zindex: 1005;
$reveal-overlay-background: rgba($black, 0.45);
// 30. Slider
// ----------
$slider-width-vertical: 0.3125rem;
$slider-transition: all 0.2s ease-in-out;
$slider-height: 0.3125rem;
$slider-background: $light-gray;
$slider-fill-background: $medium-gray;
$slider-handle-height: 0.875rem;
$slider-handle-width: 0.875rem;
$slider-handle-background: $primary-color;
$slider-opacity-disabled: 0.25;
$slider-radius: $global-radius;
// 31. Switch
// ----------
$switch-background: $light-gray;
$switch-background-active: $primary-color;
$switch-height: $space-two;
$switch-height-tiny: $space-slab;
$switch-height-small: $space-normal;
$switch-height-large: $space-large;
$switch-radius: $space-large;
$switch-margin: $global-margin;
$switch-paddle-background: $white;
$switch-paddle-offset: $space-micro;
$switch-paddle-radius: $space-large;
$switch-paddle-transition: all 0.15s ease-out;
// 32. Table
// ---------
$table-background: transparent;
$table-color-scale: 5%;
$table-border: 1px solid transparent;
$table-padding: rem-calc(8 10 10);
$table-hover-scale: 2%;
$table-row-hover: darken($table-background, $table-hover-scale);
$table-row-stripe-hover: darken($table-background,
$table-color-scale + $table-hover-scale);
$table-is-striped: false;
$table-striped-background: smart-scale($table-background, $table-color-scale);
$table-stripe: even;
$table-head-background: smart-scale($table-background, $table-color-scale / 2);
$table-head-row-hover: darken($table-head-background, $table-hover-scale);
$table-foot-background: smart-scale($table-background, $table-color-scale);
$table-foot-row-hover: darken($table-foot-background, $table-hover-scale);
$table-head-font-color: $body-font-color;
$table-foot-font-color: $body-font-color;
$show-header-for-stacked: false;
// 33. Tabs
// --------
$tab-margin: 0;
$tab-background: transparent;
$tab-background-active: transparent;
$tab-item-font-size: $font-size-small;
$tab-item-background-hover: transparent;
$tab-item-padding: $space-one $zero;
$tab-color: $primary-color;
$tab-active-color: $primary-color;
$tab-expand-max: 6;
$tab-content-background: transparent;
$tab-content-border: transparent;
$tab-content-color: foreground($tab-background, $primary-color);
$tab-content-padding: 0.625rem;
// 34. Thumbnail
// -------------
$thumbnail-border: solid 4px $white;
$thumbnail-margin-bottom: $global-margin;
$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
$thumbnail-transition: box-shadow 200ms ease-out;
$thumbnail-radius: $global-radius;
// 35. Title Bar
// -------------
$titlebar-background: $black;
$titlebar-color: $white;
$titlebar-padding: 0.3125rem;
$titlebar-text-font-weight: bold;
$titlebar-icon-color: $white;
$titlebar-icon-color-hover: $medium-gray;
$titlebar-icon-spacing: 0.15625rem;
// 36. Tooltip
// -----------
$has-tip-font-weight: $global-weight-bold;
$has-tip-border-bottom: dotted 1px $dark-gray;
$tooltip-background-color: $black;
$tooltip-color: $white;
$tooltip-padding: 0.46875rem;
$tooltip-font-size: $font-size-mini;
$tooltip-pip-width: 0.46875rem;
$tooltip-pip-height: $tooltip-pip-width * 0.866;
$tooltip-radius: $global-radius;
// 37. Top Bar
// -----------
$topbar-padding: 0.3125;
$topbar-background: $light-gray;
$topbar-submenu-background: $topbar-background;
$topbar-title-spacing: 0.3125 0.625rem 0.3125 0;
$topbar-input-width: 200px;
$topbar-unstack-breakpoint: medium;
// Internal variable that contains the flex justifying options
$-zf-flex-justify: -zf-flex-justify($global-text-direction);
$menu-items-padding: $space-one;
$xy-grid: false;
@@ -1,22 +1,64 @@
// loader class
.bg-light {
@apply bg-slate-25 dark:bg-slate-800;
}
.flex-center {
@include flex-align(center, middle);
display: flex;
}
.bottom-space-fix {
margin-bottom: auto;
}
.full-height {
@include full-height();
}
.spinner {
@include color-spinner();
@apply inline-block h-6 py-0 px-6 relative align-middle w-6;
display: inline-block;
height: $space-medium;
padding: $zero $space-medium;
position: relative;
vertical-align: middle;
width: $space-medium;
&.message {
@include normal-shadow;
@apply bg-white dark:bg-slate-800 rounded-full left-0 my-3 mx-auto p-4 top-0;
background: $color-white;
border-radius: $space-large;
left: 0;
margin: $space-slab auto;
padding: $space-normal;
top: 0;
&::before {
@apply -ml-3 -mt-3;
margin-left: -$space-slab;
margin-top: -$space-slab;
}
}
&.small {
@apply h-4 w-4;
height: $space-normal;
width: $space-normal;
&::before {
@apply h-4 -mt-2 w-4;
height: $space-normal;
margin-top: -$space-small;
width: $space-normal;
}
}
}
.justify-space-between {
justify-content: space-between;
}
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
@@ -1,19 +1,5 @@
// scss-lint:disable SpaceAfterPropertyColon
// @import 'shared/assets/fonts/inter';
html,
body {
font-family:
'PlusJakarta',
Inter,
-apple-system,
system-ui,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
sans-serif !important;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
height: 100%;
@@ -131,12 +131,37 @@
}
}
.search-header--wrap {
.search--input {
text-align: right;
}
.layout-switch__container {
transform: rotate(180deg);
}
}
// Basic filter dropdown
.basic-filter {
left: 0;
right: unset;
}
// Card label
.label-container {
.label {
margin-left: var(--space-smaller);
margin-right: 0;
}
}
// Secondary sidebar toggle button
.toggle-sidebar {
margin-left: 0;
margin-right: var(--space-minus-small);
transform: rotate(180deg);
}
// Bulk actions
.bulk-action__container {
.triangle {
@@ -177,6 +202,22 @@
}
}
// Notification panel
.notification-wrap {
left: 0;
right: var(--space-jumbo);
.action-button {
margin-left: var(--space-small);
margin-right: 0;
}
.notification-content--wrap {
margin-left: 0;
margin-right: var(--space-small);
}
}
// Help center
.article-container .row--article-block {
td:last-child {
@@ -283,6 +324,10 @@
// Other changes
.account-selector--wrap {
direction: initial;
}
.colorpicker--chrome {
direction: initial;
}
@@ -302,4 +347,9 @@
.contact--form .input-group {
direction: initial;
}
// scss-lint:disable QualifyingElement
.dropdown-menu--header > span.title {
text-align: right;
}
}
@@ -0,0 +1,33 @@
.page-title {
font-size: $font-size-big;
}
.page-sub-title {
font-size: $font-size-large;
word-wrap: break-word;
}
.block-title {
font-size: $font-size-medium;
}
.sub-block-title {
font-size: $font-size-default;
}
.text-block-title {
font-size: $font-size-small;
}
.text-muted {
color: var(--s-300);
}
a {
font-size: $font-size-small;
}
p {
font-size: $font-size-small;
word-spacing: .12em;
}
@@ -0,0 +1,73 @@
.margin-bottom-small {
margin-bottom: var(--space-small);
}
.margin-right-smaller {
margin-right: var(--space-smaller);
}
.margin-left-minus-slab {
margin-left: var(--space-minus-slab);
}
.margin-right-minus-slab {
margin-right: var(--space-minus-slab);
}
.fs-small {
font-size: var(--font-size-small);
}
.fs-default {
font-size: var(--font-size-default);
}
.fw-medium {
font-weight: var(--font-weight-medium);
}
.p-normal {
padding: var(--space-normal);
}
.overflow-scroll {
overflow: scroll;
}
.overflow-auto {
overflow: auto;
}
.overflow-hidden {
overflow: hidden;
}
.border-right {
@apply border-r border-slate-50 dark:border-slate-700;
}
.border-left {
border-left: 1px solid var(--color-border);
}
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.flex-between {
align-items: center;
display: flex;
justify-content: space-between;
}
.flex-end {
display: flex;
justify-content: end;
}
.flex-align-center {
align-items: center;
display: flex;
}
@@ -15,28 +15,63 @@
@import 'variables';
@import 'mixins';
@import 'foundation-settings';
@import 'helper-classes';
@import 'formulate';
@import 'date-picker';
@import 'foundation-sites/scss/foundation';
@include foundation-everything($flex: true);
@include foundation-prototype-text-utilities;
@include foundation-prototype-text-transformation;
@include foundation-prototype-text-decoration;
@include foundation-prototype-font-styling;
@include foundation-prototype-list-style-type;
@include foundation-prototype-rounded;
@include foundation-prototype-bordered;
@include foundation-prototype-shadow;
@include foundation-prototype-separator;
@include foundation-prototype-overflow;
@include foundation-prototype-display;
@include foundation-prototype-position;
@include foundation-prototype-border-box;
@include foundation-prototype-border-none;
@include foundation-prototype-sizing;
@include foundation-prototype-spacing;
@import 'typography';
@import 'layout';
@import 'animations';
@import 'foundation-custom';
@import 'rtl';
@import 'widgets/base';
@import 'widgets/buttons';
@import 'widgets/conv-header';
@import 'widgets/conversation-card';
@import 'widgets/conversation-view';
@import 'widgets/forms';
@import 'widgets/login';
@import 'widgets/modal';
@import 'widgets/reply-box';
@import 'widgets/report';
@import 'widgets/search-box';
@import 'widgets/sidemenu';
@import 'widgets/snackbar';
@import 'widgets/states';
@import 'widgets/status-bar';
@import 'widgets/tabs';
@import 'widgets/woot-tables';
@import 'views/settings/inbox';
@import 'views/settings/integrations';
@import 'plugins/multiselect';
@import 'plugins/dropdown';
@import '~shared/assets/stylesheets/ionicons';
@import 'utility-helpers';
.tooltip {
@apply bg-slate-900 text-white py-1 px-2 z-40 text-xs rounded-md dark:bg-slate-200 dark:text-slate-900;
}
.hide {
@apply hidden;
}
@@ -1,7 +1,46 @@
.dropdown-pane {
@apply border rounded-lg hidden relative invisible shadow-lg border-slate-25 dark:border-slate-700 box-content p-2 w-fit z-[9999];
@include elegant-card;
@include border-light;
box-sizing: content-box;
padding: var(--space-small);
width: fit-content;
z-index: var(--z-index-very-high);
&.dropdown-pane--open {
@apply bg-white absolute dark:bg-slate-800 block visible;
@apply bg-white dark:bg-slate-800;
display: block;
visibility: visible;
}
&.dropdowm--bottom {
&::before {
@include arrow(top, var(--color-border-light), 14px);
position: absolute;
right: 6px;
top: -14px;
}
&::after {
@include arrow(top, $color-white, var(--space-slab));
position: absolute;
right: var(--space-small);
top: -12px;
}
}
&.dropdowm--top {
&::before {
@include arrow(bottom, var(--color-border-light), 14px);
bottom: -14px;
position: absolute;
right: 6px;
}
&::after {
@include arrow(bottom, $color-white, var(--space-slab));
bottom: -12px;
position: absolute;
right: var(--space-small);
}
}
}
@@ -97,10 +97,6 @@
.multiselect__tags {
@apply bg-white dark:bg-slate-900 border border-solid border-slate-200 dark:border-slate-600 m-0 min-h-[2.875rem] pt-0;
input {
@apply border-0 border-none;
}
}
.multiselect__tags-wrap {
@@ -153,6 +149,7 @@
}
.multiselect-wrap--small {
.multiselect__tags,
.multiselect__input,
.multiselect {
@@ -183,6 +180,7 @@
.multiselect--disabled .multiselect__select {
@apply bg-transparent;
}
}
.multiselect-wrap--medium {
@@ -13,14 +13,20 @@
@import '~shared/assets/stylesheets/ionicons';
@import 'mixins';
@import 'foundation-settings';
@import 'helper-classes';
@import 'foundation-sites/scss/foundation';
@include foundation-prototype-spacing;
@include foundation-everything($flex: true);
@import 'typography';
@import 'layout';
@import 'animations';
@import 'foundation-custom';
@import 'widgets/buttons';
@import 'widgets/base';
@import 'widgets/forms';
@import 'plugins/multiselect';
@@ -30,6 +36,7 @@
@import 'tailwindcss/utilities';
@import 'widget/assets/scss/utilities';
html,
body {
font-family: 'PlusJakarta', sans-serif;
@@ -1 +1,112 @@
// to be removed
.settings {
@apply overflow-auto;
}
.wizard-box {
.item {
@apply cursor-pointer py-4 pr-4 pl-6 relative;
&::before,
&::after {
@apply bg-slate-75 dark:bg-slate-600 content-[''] h-full absolute top-5 w-0.5;
}
&::before {
@apply h-4 top-0;
}
&:first-child {
&::before {
@apply h-0;
}
}
&:last-child {
&::after {
@apply h-0;
}
}
&.active {
h3 {
@apply text-woot-500 dark:text-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
}
&.over {
&::after {
@apply bg-woot-500 dark:bg-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
& + .item {
&::before {
@apply bg-woot-500 dark:bg-woot-500;
}
}
}
h3 {
@apply text-slate-800 dark:text-slate-100 text-base pl-6;
}
.completed {
@apply text-green-500 dark:text-green-500 ml-1;
}
p {
@apply text-slate-600 dark:text-slate-300 text-sm m-0 pl-6;
}
.step {
@apply bg-slate-75 dark:bg-slate-600 rounded-2xl font-medium w-4 left-4 leading-4 z-[999] absolute text-center text-white dark:text-white text-xxs top-5;
i {
@apply text-xxs;
}
}
}
}
.wizard-body {
@apply border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6;
&.height-auto {
@apply h-auto;
}
}
.settings--content {
@apply my-2 mx-8;
.title {
@apply font-medium;
}
.code {
@apply bg-slate-50 dark:bg-slate-800 overflow-auto p-2.5 whitespace-nowrap;
code {
@apply bg-transparent border-0;
}
}
}
.login-init {
@apply pt-[30%] text-center;
p {
@apply p-6;
}
> a > img {
@apply w-60;
}
}
@@ -1,146 +0,0 @@
// scss-lint:disable QualifyingElement
// Base typography
h1,
h2,
h3,
h4,
h5,
h6 {
@apply font-medium text-slate-800 dark:text-slate-50;
}
p {
text-rendering: optimizeLegibility;
word-spacing: 0.12em;
@apply mb-2 leading-[1.65] text-sm;
a {
@apply text-woot-500 dark:text-woot-500 cursor-pointer;
}
}
a {
@apply text-sm;
}
hr {
@apply clear-both max-w-full h-0 my-5 mx-0 border-slate-300 dark:border-slate-600;
}
ul,
ol,
dl {
@apply mb-2 list-disc list-outside leading-[1.65];
}
// Form elements
label {
@apply text-slate-800 dark:text-slate-200 block m-0 leading-7 text-sm font-medium;
&.error {
input {
@apply mb-1;
}
}
}
.input-wrap,
.help-text {
@apply text-slate-800 dark:text-slate-100 text-sm font-medium;
.help-text {
@apply font-normal text-slate-600 dark:text-slate-400;
}
}
// Focus outline removal
.button,
textarea,
input:focus {
outline: none;
}
// Inputs
input[type='text'],
input[type='number'],
input[type='password'],
input[type='date'],
input[type='email'],
input[type='url'] {
@apply block box-border w-full transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] h-10 appearance-none mx-0 mt-0 mb-4 p-2 rounded-md text-base font-normal bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
&[disabled] {
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600 cursor-not-allowed;
}
}
input[type='file'] {
@apply bg-white dark:bg-slate-800 leading-[1.15] mb-4;
}
// Select
select {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='32' height='24' viewBox='0 0 32 24'><polygon points='0,0 32,0 16,24' style='fill: rgb%28110, 111, 115%29'></polygon></svg>");
background-position: right -1rem center;
background-size: 9px 6px;
@apply h-10 mx-0 mt-0 mb-4 bg-origin-content focus-visible:outline-none bg-no-repeat py-2 pr-6 pl-2 rounded-md w-full text-base font-normal appearance-none transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
}
// Textarea
textarea {
@apply block box-border w-full transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] h-16 appearance-none mx-0 mt-0 mb-4 p-2 rounded-md text-base font-normal bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
&[disabled] {
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600 cursor-not-allowed;
}
}
// Error handling
.has-multi-select-error {
div.multiselect {
@apply mb-1;
}
}
.error {
input,
input:not([type]),
textarea,
select,
.multiselect > .multiselect__tags,
.multiselect:not(.no-margin) {
@apply border border-solid border-red-400 dark:border-red-400 mb-1;
}
.message {
@apply text-red-400 dark:text-red-400 block text-sm mb-2.5 w-full;
}
}
.input-group.small {
input {
@apply text-sm h-8;
}
.error {
@apply border-red-400 dark:border-red-400;
}
}
// Code styling
code {
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
'"Liberation Mono"', '"Courier New"', 'monospace';
@apply text-xs border-0;
&.hljs {
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-50 rounded-lg p-5;
.hljs-number,
.hljs-string {
@apply text-red-800 dark:text-red-400;
}
}
}
@@ -1,40 +1,8 @@
// scss-lint:disable SpaceAfterPropertyColon
// scss-lint:disable MergeableSelector
button {
font-family: inherit;
transition:
background-color 0.25s ease-out,
color 0.25s ease-out;
@apply inline-block items-center mb-0 text-center align-middle cursor-pointer text-sm mt-0 mx-0 py-1 px-2.5 border border-solid border-transparent dark:border-transparent rounded-[0.3125rem];
&:disabled,
&.disabled {
@apply opacity-40 cursor-not-allowed;
}
}
.button-group {
@apply mb-0 flex flex-nowrap items-stretch;
.button {
flex: 0 0 auto;
@apply m-0 text-sm rounded-none first:rounded-tl-[0.3125rem] first:rounded-bl-[0.3125rem] last:rounded-tr-[0.3125rem] last:rounded-br-[0.3125rem] rtl:space-x-reverse;
}
.button--only-icon {
@apply w-10 justify-center pl-0 pr-0;
}
}
.back-button {
@apply m-0;
}
.button {
@apply items-center bg-woot-500 dark:bg-woot-500 px-2.5 text-white dark:text-white inline-flex h-10 mb-0 gap-2 font-medium;
@apply items-center inline-flex h-10 mb-0 gap-2;
.button__content {
@apply w-full whitespace-nowrap overflow-hidden text-ellipsis;
@apply w-full;
img,
svg {
@@ -42,61 +10,12 @@ button {
}
}
&:hover {
@apply bg-woot-600 dark:bg-woot-600;
}
&:disabled,
&.disabled {
@apply opacity-40 cursor-not-allowed;
}
&.success {
@apply bg-[#44ce4b] dark:bg-[#44ce4b] text-white dark:text-white;
}
&.secondary {
@apply bg-slate-700 dark:bg-slate-600 text-white dark:text-white;
}
&.primary {
@apply bg-woot-500 dark:bg-woot-500 text-white dark:text-white;
}
&.clear {
@apply text-woot-500 dark:text-woot-500 bg-transparent dark:bg-transparent;
}
&.alert {
@apply bg-red-500 dark:bg-red-500 text-white dark:text-white;
&.clear {
@apply bg-transparent dark:bg-transparent;
}
}
&.warning {
@apply bg-[#ffc532] dark:bg-[#ffc532] text-white dark:text-white;
&.clear {
@apply bg-transparent dark:bg-transparent;
}
}
&.tiny {
@apply h-6 text-[10px];
}
&.small {
@apply h-8 text-xs;
}
.spinner {
@apply px-2 py-0;
}
// @TODDO - Remove after moving all buttons to woot-button
.icon+.button__content {
.icon + .button__content {
@apply w-auto;
}
@@ -115,7 +34,7 @@ button {
}
&.hollow {
@apply border border-woot-500 bg-transparent dark:bg-transparent dark:border-woot-500 text-woot-500 dark:text-woot-500 hover:bg-woot-50 dark:hover:bg-woot-900;
@apply border border-woot-500 dark:border-woot-500 text-woot-500 dark:text-woot-500 hover:bg-woot-50 dark:hover:bg-woot-900;
&.secondary {
@apply text-slate-700 border-slate-200 dark:border-slate-600 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-700;
@@ -0,0 +1 @@
// File to be removed
@@ -0,0 +1,16 @@
@keyframes left-shift-animation {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(1px);
}
}
.conversation {
&.active {
animation: left-shift-animation 0.25s $swift-ease-out-function;
}
}
@@ -79,7 +79,7 @@
@apply rounded-r-lg rounded-l mr-auto break-words;
&:not(.is-unsupported) {
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50;
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50
}
&.is-image {
@@ -91,7 +91,7 @@
}
.file {
.attachment-name {
.text-block-title {
@apply text-slate-700 dark:text-woot-300;
}
@@ -222,6 +222,20 @@
@apply flex relative flex-col;
}
.typing-indicator-wrap {
@apply items-center flex h-0 absolute w-full -top-8;
.typing-indicator {
@include elegant-card;
@include round-corner;
@apply py-2 pr-4 pl-5 bg-white dark:bg-slate-700 text-slate-800 dark:text-slate-100 text-xs font-semibold my-2.5 mx-auto;
.gif {
@apply ml-2 w-6;
}
}
}
.left .bubble .text-content {
h1,
h2,
@@ -0,0 +1,78 @@
// scss-lint:disable QualifyingElement
label {
@apply text-slate-800 dark:text-slate-200;
}
textarea {
@apply bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
}
input {
@apply bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
&[disabled] {
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600;
}
}
input[type='file'] {
@apply bg-white dark:bg-slate-800;
}
select {
@apply bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
}
.error {
input[type='color'],
input[type='date'],
input[type='datetime'],
input[type='datetime-local'],
input[type='email'],
input[type='month'],
input[type='number'],
input[type='password'],
input[type='search'],
input[type='tel'],
input[type='text'],
input[type='time'],
input[type='url'],
input[type='week'],
input:not([type]),
textarea,
select,
.multiselect > .multiselect__tags {
@apply border border-solid border-red-400 dark:border-red-400;
}
.message {
@apply text-red-400 dark:text-red-400 block text-sm mb-2.5 w-full;
}
}
.button,
textarea,
input {
&:focus {
outline: none;
}
}
.input-wrap {
@apply text-slate-800 dark:text-slate-100 text-sm font-medium;
}
.help-text {
@apply font-normal text-slate-600 dark:text-slate-400;
}
.input-group.small {
input {
@apply text-sm h-8;
}
.error {
@apply border-red-400 dark:border-red-400;
}
}
@@ -0,0 +1,66 @@
.auth-wrap {
width: 100%;
}
// Outside login wrapper
.login {
@include full-height;
overflow-y: auto;
padding-top: $space-larger * 1.2;
.login__hero {
margin-bottom: $space-larger;
.hero__logo {
width: 180px;
}
.hero__title {
font-weight: $font-weight-light;
margin-top: $space-larger;
}
.hero__sub {
color: $medium-gray;
font-size: $font-size-medium;
}
}
// Login box
.login-box {
@include background-white;
@include border-normal;
@include elegant-card;
border-radius: $space-smaller;
padding: $space-large;
label {
color: $color-gray;
font-size: $font-size-default;
input {
font-size: $font-size-default;
height: $space-larger;
padding: $space-slab;
}
.error {
font-size: $font-size-small;
}
}
.button {
height: $space-larger;
}
}
.sigin__footer {
font-size: $font-size-default;
padding: $space-medium;
> a {
font-weight: $font-weight-bold;
}
}
}
@@ -0,0 +1,75 @@
.modal-mask {
// @include flex;
// @include flex-align(center, middle);
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
}
.page-top-bar {
@apply px-8 pt-9 pb-0;
img {
@apply max-h-[3.75rem];
}
}
.modal-container {
@apply shadow-md rounded-sm max-h-full overflow-auto relative w-[37.5rem];
&.medium {
@apply max-w-[80%] w-[56.25rem];
}
.content-box {
@apply h-auto p-0;
}
h2 {
@apply text-slate-800 dark:text-slate-100 text-lg font-semibold;
}
p {
@apply text-sm m-0 p-0 text-slate-600 mt-2 text-sm dark:text-slate-300;
}
.content {
@apply p-8;
}
form,
.modal-content {
@apply pt-4 pb-8 px-8 self-center;
a {
@apply p-4;
}
}
.modal-footer {
// @include flex;
// @include flex-align($x: flex-end, $y: middle);
@apply flex justify-end items-center py-2 px-0 gap-2;
&.justify-content-end {
@apply justify-end;
}
}
.delete-item {
@apply p-8;
button {
@apply m-0;
}
}
}
.modal-enter,
.modal-leave {
@apply opacity-0;
}
.modal-enter .modal-container,
.modal-leave .modal-container {
transform: scale(1.1);
// @apply transform scale-110;
}
@@ -0,0 +1,60 @@
.reply-box {
transition: box-shadow 0.35s $swift-ease-out-function,
height 2s $swift-ease-out-function;
&.is-focused {
box-shadow: var(--shadow);
}
.reply-box__top {
.icon {
color: $medium-gray;
cursor: pointer;
font-size: $font-size-medium;
margin-right: $space-small;
&.active {
color: $color-woot;
}
}
.attachment {
cursor: pointer;
margin-right: $space-one;
padding: 0 $space-small;
}
.video-js {
background: transparent;
// Override min-height : 50px in foundation
//
max-height: $space-mega * 2.4;
min-height: 3rem;
padding: var(--space-normal) 0 0;
resize: none;
}
> textarea {
@include ghost-input();
background: transparent;
margin: 0;
max-height: $space-mega * 2.4;
// Override min-height : 50px in foundation
min-height: 3rem;
padding: var(--space-normal) 0 0;
resize: none;
}
}
&.is-private {
@apply bg-yellow-100 dark:bg-yellow-800;
.reply-box__top {
@apply bg-yellow-100 dark:bg-yellow-800;
> input {
@apply bg-yellow-100 dark:bg-yellow-800;
}
}
}
}
@@ -0,0 +1,60 @@
.report-card {
@include custom-border-top(3px, transparent);
cursor: pointer;
margin: 0;
padding: var(--space-normal);
&.active {
@include custom-border-top(3px, var(--color-woot));
@include background-white;
.heading,
.metric {
color: var(--color-woot);
}
}
.heading {
align-items: center;
color: var(--color-heading);
display: flex;
font-size: var(--font-size-small);
font-weight: var(--font-weight-bold);
margin: 0;
}
.info-icon {
color: var(--b-400);
margin-left: var(--space-micro);
}
.metric-wrap {
align-items: center;
display: flex;
}
.metric {
font-size: var(--font-size-big);
font-weight: var(--font-weight-feather);
margin-top: var(--space-smaller);
}
.metric-trend {
font-size: var(--font-size-small);
margin: 0 var(--space-small);
}
.metric-up {
color: $success-color;
}
.metric-down {
color: $alert-color;
}
.desc {
font-size: var(--font-size-small);
margin: 0;
text-transform: capitalize;
}
}
@@ -0,0 +1,29 @@
.reports-option__rounded--item {
border-radius: 100%;
height: var(--space-two);
width: var(--space-two);
}
.reports-option__item {
flex-shrink: 0;
margin-right: var(--space-small);
}
.reports-option__label--swatch {
border: 1px solid var(--color-border);
}
.reports-option__wrap {
align-items: center;
display: flex;
}
.reports-option__title {
margin: 0 var(--space-small);
}
.switch {
margin-bottom: var(--space-zero);
margin-left: var(--space-small);
}
@@ -0,0 +1,18 @@
.search {
@include flex;
@include flex-align($x: left, $y: middle);
@include flex-shrink;
padding: $space-one $space-normal;
transition: all 0.3s var(--ease-in-out-quad);
> .icon {
color: $medium-gray;
font-size: $font-size-medium;
}
> input {
@include ghost-input();
margin: 0;
}
}
@@ -0,0 +1,78 @@
.side-menu {
i {
margin-right: var(--space-smaller);
min-width: var(--space-two);
}
}
.sidebar {
z-index: 1024 - 1;
//logo
.logo {
img {
max-height: 108px;
padding: $woot-logo-padding;
}
}
.nested {
a {
font-size: var(--font-size-small);
margin-bottom: var(--space-micro);
margin-top: var(--space-micro);
.inbox-icon {
display: inline-block;
margin-right: var(--space-micro);
min-width: var(--space-normal);
text-align: center;
}
}
}
}
// bottom-nav
.bottom-nav {
@include flex;
@include space-between-column;
@include border-normal-top;
flex-direction: column;
padding: var(--space-one) var(--space-normal) var(--space-one)
var(--space-one);
position: relative;
&:hover {
background: var(--color-background-light);
}
.dropdown-pane {
bottom: 3.75rem;
display: block;
visibility: visible;
width: fit-content;
}
.active {
border-bottom: 2px solid $medium-gray;
}
}
.hamburger--menu {
cursor: pointer;
display: block;
margin-right: var(--space-normal);
}
.header--icon {
display: block;
margin: 0 var(--space-small) 0 var(--space-smaller);
@media screen and (max-width: 1200px) {
display: none;
}
}
.header-title {
margin: 0 var(--space-small);
}
@@ -0,0 +1,45 @@
.ui-snackbar-container {
left: 0;
margin: 0 auto;
max-width: 25rem;
overflow: hidden;
position: absolute;
right: 0;
text-align: center;
top: $space-normal;
z-index: 9999;
}
.ui-snackbar {
@include shadow;
background-color: $woot-snackbar-bg;
border-radius: $space-smaller;
display: inline-flex;
margin-bottom: $space-small;
max-width: 25rem;
min-height: 1.875rem;
min-width: 15rem;
padding: $space-slab $space-medium;
text-align: left;
}
.ui-snackbar-text {
color: $color-white;
font-size: $font-size-small;
font-weight: $font-weight-medium;
}
.ui-snackbar-action {
margin-left: auto;
padding-left: 1.875rem;
button {
background: none;
border: 0;
color: $woot-snackbar-button;
font-size: $font-size-small;
margin: 0;
padding: 0;
text-transform: uppercase;
}
}
@@ -0,0 +1 @@
// To be removed
@@ -0,0 +1,46 @@
.status-bar {
@include flex;
@include flex-align($x: center, $y: middle);
background: lighten($warning-color, 36%);
flex-direction: column;
margin: 0;
padding: $space-normal $space-smaller;
.message {
font-weight: $font-weight-medium;
margin-bottom: $zero;
}
.button {
margin: $space-smaller $zero $zero;
padding: $space-small $space-normal;
}
&.danger {
background: lighten($alert-color, 30%);
.button {
// Default and disabled states
&,
&.disabled,
&[disabled],
&.disabled:hover,
&[disabled]:hover,
&.disabled:focus,
&[disabled]:focus {
background-color: $alert-color;
color: $color-white;
}
&:hover,
&:focus {
background-color: darken($alert-color, 7%);
color: $color-white;
}
}
}
&.warning {
background: lighten($warning-color, 36%);
}
}
@@ -7,7 +7,7 @@
}
.tabs {
@apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4 list-none mb-0;
@apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4;
}
.tabs--with-scroll {
@@ -31,7 +31,7 @@
}
.tabs-title {
@apply flex-shrink-0 my-0 mx-2;
@apply flex-shrink-0 my-0 mx-2 ;
.badge {
@apply bg-slate-50 dark:bg-slate-800 rounded-md text-slate-600 dark:text-slate-100 h-5 flex items-center justify-center text-xxs font-semibold my-0 mx-1 px-1 py-0;
@@ -53,7 +53,7 @@
}
a {
@apply flex items-center flex-row border-b py-2.5 select-none cursor-pointer border-transparent text-slate-500 dark:text-slate-200 text-sm top-[1px] relative;
@apply flex items-center flex-row border-b border-transparent text-slate-500 dark:text-slate-200 text-sm top-[1px] relative;
transition: border-color 0.15s $swift-ease-out-function;
}
@@ -1,9 +1,9 @@
table {
@apply border-spacing-0 text-sm w-full;
@apply border-spacing-0 text-sm;
thead {
th {
@apply font-semibold tracking-[1px] text-left px-2.5 uppercase text-slate-900 dark:text-slate-200;
@apply font-semibold tracking-[1px] text-left uppercase text-slate-900 dark:text-slate-200;
}
}
@@ -1,7 +1,7 @@
<template>
<div class="-mt-px text-sm">
<button
class="flex items-center select-none w-full rounded-none bg-slate-50 dark:bg-slate-800 border border-l-0 border-r-0 border-solid m-0 border-slate-100 dark:border-slate-700/50 cursor-grab justify-between py-2 px-4 drag-handle"
class="flex items-center select-none w-full bg-slate-50 dark:bg-slate-800 border border-l-0 border-r-0 border-solid m-0 border-slate-100 dark:border-slate-700/50 cursor-grab justify-between py-2 px-4 drag-handle"
@click="$emit('click')"
>
<div class="flex justify-between mb-0.5">
@@ -1,12 +1,10 @@
<template>
<button
class="sm:w-[50%] md:w-1/3 lg:w-1/4 bg-white dark:bg-slate-900 cursor-pointer flex flex-col transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-slate-25 dark:border-slate-800 hover:border-woot-500 dark:hover:border-woot-500 hover:shadow-md hover:z-50 disabled:opacity-60"
class="sm:w-[50%] md:w-[34%] lg:w-[25%] channel"
@click="$emit('click')"
>
<img :src="src" :alt="title" class="w-[50%] my-4 mx-auto" />
<h3
class="text-slate-800 dark:text-slate-100 text-base text-center capitalize"
>
<img :src="src" :alt="title" />
<h3 class="channel__title">
{{ title }}
</h3>
</button>
@@ -33,8 +31,32 @@ export default {
filter: grayscale(100%);
}
&:hover {
&.channel:hover {
@apply border-transparent shadow-none cursor-not-allowed;
}
}
.channel {
@apply bg-white dark:bg-slate-900 cursor-pointer flex flex-col transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-slate-25 dark:border-slate-800;
&:hover {
@apply border-woot-500 dark:border-woot-500 shadow-md z-50;
}
&.disabled {
@apply opacity-60;
}
img {
@apply w-[50%] my-4 mx-auto;
}
.channel__title {
@apply text-slate-800 dark:text-slate-100 text-base text-center capitalize;
}
p {
@apply text-slate-600 dark:text-slate-300 w-full text-sm;
}
}
</style>
@@ -16,7 +16,7 @@
>
<div class="flex max-w-[85%] justify-center items-center">
<h1
class="text-xl break-words overflow-hidden whitespace-nowrap font-medium text-ellipsis text-black-900 dark:text-slate-100 mb-0"
class="text-xl break-words overflow-hidden whitespace-nowrap text-ellipsis text-black-900 dark:text-slate-100 mb-0"
:title="pageTitle"
>
{{ pageTitle }}
@@ -143,10 +143,7 @@
<div v-if="chatListLoading" class="text-center">
<span class="spinner mt-4 mb-4" />
</div>
<p
v-if="showEndOfListMessage"
class="text-center text-slate-400 dark:text-slate-300 p-4"
>
<p v-if="showEndOfListMessage" class="text-center text-muted p-4">
{{ $t('CHAT_LIST.EOF') }}
</p>
<intersection-observer
@@ -1,7 +1,7 @@
<template>
<div class="py-3 px-4">
<div class="flex items-center mb-1">
<h4 class="text-sm flex items-center m-0 w-full error">
<h4 class="text-block-title flex items-center m-0 w-full error">
<div v-if="isAttributeTypeCheckbox" class="checkbox-wrap">
<input
v-model="editedValue"
@@ -12,7 +12,7 @@
</div>
<div class="flex items-center justify-between w-full">
<span
class="attribute-name w-full text-slate-800 dark:text-slate-100 font-medium text-sm mb-0"
class="attribute-name"
:class="{ error: $v.editedValue.$error }"
>
{{ label }}
@@ -32,24 +32,19 @@
</div>
<div v-if="notAttributeTypeCheckboxAndList">
<div v-show="isEditing">
<div class="mb-2 w-full flex items-center">
<div class="input-group small">
<input
ref="inputfield"
v-model="editedValue"
:type="inputType"
class="!h-8 ltr:rounded-r-none rtl:rounded-l-none !mb-0 !text-sm"
class="input-group-field"
autofocus="true"
:class="{ error: $v.editedValue.$error }"
@blur="$v.editedValue.$touch"
@keyup.enter="onUpdate"
/>
<div>
<woot-button
size="small"
icon="checkmark"
class="rounded-l-none rtl:rounded-r-none"
@click="onUpdate"
/>
<div class="input-group-button">
<woot-button size="small" icon="checkmark" @click="onUpdate" />
</div>
</div>
<span
@@ -311,6 +306,7 @@ export default {
@apply my-0 mr-2 ml-0;
}
.attribute-name {
@apply w-full text-slate-800 dark:text-slate-100;
&.error {
@apply text-red-400 dark:text-red-500;
}
+19 -36
View File
@@ -62,9 +62,9 @@ export default {
computed: {
modalContainerClassName() {
let className =
'modal-container rtl:text-right shadow-md rounded-sm max-h-full overflow-auto relative w-[37.5rem] bg-white dark:bg-slate-800 skip-context-menu';
'modal-container bg-white dark:bg-slate-800 skip-context-menu';
if (this.fullWidth) {
return `${className} items-center rounded-none flex h-full justify-center w-full`;
return `${className} modal-container--full-width`;
}
return `${className} ${this.size}`;
@@ -109,43 +109,26 @@ export default {
};
</script>
<style lang="scss">
.modal-mask {
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
<style scoped lang="scss">
.modal-container--full-width {
align-items: center;
border-radius: 0;
display: flex;
height: 100%;
justify-content: center;
width: 100%;
}
.modal-mask.right-aligned {
justify-content: flex-end;
.modal-container {
&.medium {
@apply max-w-[80%] w-[56.25rem];
}
// .content-box {
// @apply h-auto p-0;
// }
.content {
@apply p-8;
}
form,
.modal-content {
@apply pt-4 pb-8 px-8 self-center;
a {
@apply p-4;
}
}
border-radius: 0;
height: 100%;
width: 30rem;
}
}
.modal-big {
@apply w-full;
}
.modal-mask.right-aligned {
@apply justify-end;
.modal-container {
@apply rounded-none h-full w-[30rem];
}
}
.modal-enter,
.modal-leave {
@apply opacity-0;
}
.modal-enter .modal-container,
.modal-leave .modal-container {
transform: scale(1.1);
width: 60%;
}
</style>
@@ -3,7 +3,7 @@
<img v-if="headerImage" :src="headerImage" alt="No image" />
<h2
ref="modalHeaderTitle"
class="text-slate-800 text-lg font-semibold dark:text-slate-50"
class="text-slate-800 text-lg dark:text-slate-100"
>
{{ headerTitle }}
</h2>
@@ -1,21 +1,19 @@
<template>
<div
class="ml-0 mr-0 flex py-8 w-full xl:w-3/4 flex-col xl:flex-row"
class="ml-0 mr-0 flex pt-0 pr-4 pb-4 pl-0"
:class="{
'border-b border-solid border-slate-50 dark:border-slate-700/30':
'pt-4 border-b border-solid border-slate-50 dark:border-slate-700/30':
showBorder,
}"
>
<div class="w-full xl:w-1/4 min-w-0 xl:max-w-[30%] pr-12">
<div class="w-[30%] min-w-0 max-w-[30%] pr-12">
<p
v-if="title"
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
>
{{ title }}
</p>
<p
class="text-sm mb-2 text-slate-700 dark:text-slate-300 leading-5 tracking-normal mt-2"
>
<p class="text-sm mb-2">
<slot v-if="subTitle" name="subTitle">
{{ subTitle }}
</slot>
@@ -25,7 +23,7 @@
{{ note }}
</p>
</div>
<div class="w-full xl:w-1/2 min-w-0 xl:max-w-[50%]">
<div class="w-[50%] min-w-0 max-w-[50%]">
<slot />
</div>
</div>
@@ -1,17 +1,11 @@
<template>
<div>
<div
class="shadow-sm bg-slate-800 dark:bg-slate-700 rounded-[4px] items-center gap-3 inline-flex mb-2 max-w-[25rem] min-h-[1.875rem] min-w-[15rem] px-6 py-3 text-left"
>
<div class="text-white dark:text-white text-sm font-medium">
<div class="ui-snackbar">
<div class="ui-snackbar-text">
{{ message }}
</div>
<div v-if="action">
<router-link
v-if="action.type == 'link'"
:to="action.to"
class="text-woot-500 dark:text-woot-500 cursor-pointer font-medium hover:text-woot-600 dark:hover:text-woot-600 select-none"
>
<div v-if="action" class="ui-snackbar-action">
<router-link v-if="action.type == 'link'" :to="action.to">
{{ action.message }}
</router-link>
</div>
@@ -1,9 +1,5 @@
<template>
<transition-group
name="toast-fade"
tag="div"
class="left-0 my-0 mx-auto max-w-[25rem] overflow-hidden absolute right-0 text-center top-4 z-[9999]"
>
<transition-group name="toast-fade" tag="div" class="ui-snackbar-container">
<woot-snackbar
v-for="snackMessage in snackMessages"
:key="snackMessage.key"
@@ -48,7 +48,7 @@
v-on-clickaway="closeDropdown"
class="dropdown-pane dropdown-pane--open"
>
<woot-dropdown-menu class="mb-0">
<woot-dropdown-menu>
<woot-dropdown-item v-if="!isPending">
<woot-button
variant="clear"
@@ -18,6 +18,7 @@ import Label from './ui/Label';
import LoadingState from './widgets/LoadingState';
import Modal from './Modal';
import ModalHeader from './ModalHeader';
import ReportStatsCard from './widgets/ReportStatsCard';
import SidemenuIcon from './SidemenuIcon';
import Spinner from 'shared/components/Spinner';
import SubmitButton from './buttons/FormSubmitButton';
@@ -45,6 +46,7 @@ const WootUIKit = {
LoadingState,
Modal,
ModalHeader,
ReportStatsCard,
SidemenuIcon,
Spinner,
SubmitButton,
@@ -2,7 +2,7 @@ import { frontendURL } from '../../../../helper/URLHelper';
const campaigns = accountId => ({
parentNav: 'campaigns',
routes: ['ongoing_campaigns', 'one_off'],
routes: ['settings_account_campaigns', 'one_off'],
menuItems: [
{
icon: 'arrow-swap',
@@ -10,7 +10,7 @@ const campaigns = accountId => ({
key: 'ongoingCampaigns',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/campaigns/ongoing`),
toStateName: 'ongoing_campaigns',
toStateName: 'settings_account_campaigns',
},
{
key: 'oneOffCampaigns',
@@ -2,15 +2,6 @@ import { FEATURE_FLAGS } from '../../../../featureFlags';
import { frontendURL } from '../../../../helper/URLHelper';
const primaryMenuItems = accountId => [
{
icon: 'mail-inbox',
key: 'inboxView',
label: 'INBOX_VIEW',
featureFlag: FEATURE_FLAGS.INBOX_VIEW,
toState: frontendURL(`accounts/${accountId}/inbox-view`),
toStateName: 'inbox_view',
roles: ['administrator', 'agent'],
},
{
icon: 'chat',
key: 'conversations',
@@ -43,7 +34,7 @@ const primaryMenuItems = accountId => [
label: 'CAMPAIGNS',
featureFlag: FEATURE_FLAGS.CAMPAIGNS,
toState: frontendURL(`accounts/${accountId}/campaigns`),
toStateName: 'ongoing_campaigns',
toStateName: 'settings_account_campaigns',
roles: ['administrator'],
},
{
@@ -1,4 +1,3 @@
import { FEATURE_FLAGS } from '../../../../featureFlags';
import { frontendURL } from '../../../../helper/URLHelper';
const reports = accountId => ({
@@ -7,7 +6,6 @@ const reports = accountId => ({
'account_overview_reports',
'conversation_reports',
'csat_reports',
'bot_reports',
'agent_reports',
'label_reports',
'inbox_reports',
@@ -35,14 +33,6 @@ const reports = accountId => ({
toState: frontendURL(`accounts/${accountId}/reports/csat`),
toStateName: 'csat_reports',
},
{
icon: 'bot',
label: 'REPORTS_BOT',
hasSubMenu: false,
featureFlag: FEATURE_FLAGS.RESPONSE_BOT,
toState: frontendURL(`accounts/${accountId}/reports/bot`),
toStateName: 'bot_reports',
},
{
icon: 'people',
label: 'REPORTS_AGENT',
@@ -160,7 +160,7 @@ const settings = accountId => ({
beta: true,
},
{
icon: 'document-list-clock',
icon: 'key',
label: 'SLA',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/settings/sla/list`),
@@ -45,7 +45,7 @@
<div
v-if="globalConfig.createNewAccountFromDashboard"
class="flex justify-end items-center p-8 gap-2"
class="modal-footer delete-item"
>
<button
class="button success large expanded nice"
@@ -2,7 +2,7 @@
<woot-button
v-tooltip.right="$t(`SIDEBAR.PROFILE_SETTINGS`)"
variant="link"
class="items-center flex rounded-full"
class="current-user"
@click="handleClick"
>
<thumbnail
@@ -38,3 +38,12 @@ export default {
},
};
</script>
<style scoped lang="scss">
.current-user {
align-items: center;
display: flex;
border-radius: 50%;
border: 2px solid var(--white);
}
</style>
@@ -1,7 +1,7 @@
<template>
<div class="mb-4">
<button
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 p-0 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-700 dark:hover:text-slate-100 hover:text-slate-600 relative"
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-700 dark:hover:text-slate-100 hover:text-slate-600 relative"
:class="{
'bg-woot-50 dark:bg-slate-800 text-woot-500 hover:bg-woot-50':
isNotificationPanelActive,
@@ -16,7 +16,7 @@
/>
<span
v-if="unreadCount"
class="text-black-900 bg-yellow-300 absolute -top-0.5 -right-1 text-xxs min-w-[1rem] rounded-full"
class="text-black-900 bg-yellow-300 absolute -top-0.5 -right-1 p-1 text-xxs min-w-[1rem] rounded-full"
>
{{ unreadCount }}
</span>
@@ -53,7 +53,7 @@
</span>
<span
v-if="showChildCount"
class="bg-slate-50 dark:bg-slate-700 rounded-full min-w-[18px] justify-center items-center flex text-xxs font-medium mx-1 py-0 px-1"
class="bg-slate-50 dark:bg-slate-700 rounded text-xxs font-medium mx-1 py-0 px-1"
:class="
isCountZero
? `text-slate-300 dark:text-slate-500`
@@ -1,25 +1,18 @@
<template>
<div
class="ltr:mr-1 rtl:ml-1 mb-1"
:class="labelClass"
:style="labelStyle"
:title="description"
>
<div :class="labelClass" :style="labelStyle" :title="description">
<span v-if="icon" class="label-action--button">
<fluent-icon :icon="icon" size="12" class="label--icon cursor-pointer" />
</span>
<span
v-if="['smooth', 'dashed'].includes(variant) && title && !icon"
:style="{ background: color }"
class="label-color-dot flex-shrink-0"
class="label-color-dot"
/>
<span v-if="!href" class="whitespace-nowrap text-ellipsis overflow-hidden">
{{ title }}
</span>
<span v-if="!href">{{ title }}</span>
<a v-else :href="href" :style="anchorStyle">{{ title }}</a>
<button
v-if="showClose"
class="label-close--button p-0"
class="label-close--button"
:style="{ color: textColor }"
@click="onClick"
>
@@ -111,7 +104,7 @@ export default {
<style scoped lang="scss">
.label {
@apply inline-flex items-center font-medium text-xs rounded-[4px] gap-1 p-1 bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100 border border-solid border-slate-75 dark:border-slate-600 h-6;
@apply inline-flex items-center font-medium gap-1 mr-1 rtl:ml-1 rtl:mr-0 mb-1 p-1 bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100 border border-solid border-slate-75 dark:border-slate-600 h-6;
&.small {
@apply text-xs py-0.5 px-1 leading-tight h-5;
@@ -1,18 +1,6 @@
<template>
<div
class="flex flex-col min-w-[15rem] max-h-[21.25rem] max-w-[23.75rem] rounded-md border border-solid border-slate-75 dark:border-slate-600"
:class="{
'bg-woot-25 dark:bg-slate-700 border border-solid border-woot-300 dark:border-woot-400':
active,
}"
>
<div
class="flex justify-between items-center px-2 w-full h-10 bg-slate-50 dark:bg-slate-900 rounded-t-[5px] border-b border-solid border-slate-50 dark:border-slate-600"
:class="{
'bg-woot-50 border-b border-solid border-woot-75 dark:border-woot-700':
active,
}"
>
<div class="preview-card--wrap" :class="{ 'active-card': active }">
<div class="header--wrap" :class="{ active: active }">
<div class="items-center flex font-medium p-1 text-sm">{{ heading }}</div>
<fluent-icon
v-if="active"
@@ -68,3 +56,21 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.preview-card--wrap {
@apply flex flex-col min-w-[15rem] max-h-[21.25rem] max-w-[23.75rem] rounded-md border border-solid border-slate-75 dark:border-slate-600;
.header--wrap {
@apply flex justify-between items-center px-2 w-full h-10 bg-slate-50 dark:bg-slate-900 rounded-t-[5px] border-b border-solid border-slate-50 dark:border-slate-600;
}
.active {
@apply bg-woot-50 border-b border-solid border-woot-75 dark:border-woot-700;
}
}
.active-card {
@apply bg-woot-25 dark:bg-slate-700 border border-solid border-woot-300 dark:border-woot-400;
}
</style>
@@ -1,7 +1,7 @@
<template>
<button
type="button"
class="toggle-button p-0"
class="toggle-button"
:class="{ active: value, small: size === 'small' }"
role="switch"
:aria-checked="value.toString()"
@@ -7,7 +7,7 @@
>
<a @click="onTabClick">
{{ name }}
<div v-if="showBadge" class="badge min-w-[20px]">
<div v-if="showBadge" class="badge">
<span>
{{ getItemCount }}
</span>
@@ -13,23 +13,18 @@
>
<div class="flex items-center">
<h3
class="text-slate-800 dark:text-slate-100 text-base font-medium pl-6 overflow-hidden whitespace-nowrap mb-1.5 text-ellipsis leading-tight"
class="overflow-hidden whitespace-nowrap text-ellipsis leading-tight"
>
{{ item.title }}
</h3>
<span
v-if="isOver(item)"
class="text-green-500 dark:text-green-500 ml-1"
>
<span v-if="isOver(item)" class="completed">
<fluent-icon icon="checkmark" />
</span>
</div>
<span class="step">
{{ items.indexOf(item) + 1 }}
</span>
<p class="text-slate-600 dark:text-slate-300 text-sm m-0 pl-6">
{{ item.body }}
</p>
<p>{{ item.body }}</p>
</div>
</transition-group>
</template>
@@ -65,40 +60,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.wizard-box {
.item {
@apply cursor-pointer after:bg-slate-75 before:bg-slate-75 dark:after:bg-slate-600 dark:before:bg-slate-600 py-4 pr-4 pl-6 relative before:h-4 before:top-0 last:before:h-0 first:before:h-0 last:after:h-0 before:content-[''] before:absolute before:w-0.5 after:content-[''] after:h-full after:absolute after:top-5 after:w-0.5;
&.active {
h3 {
@apply text-woot-500 dark:text-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
}
&.over {
&::after {
@apply bg-woot-500 dark:bg-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
& + .item {
&::before {
@apply bg-woot-500 dark:bg-woot-500;
}
}
}
.step {
@apply bg-slate-75 dark:bg-slate-600 rounded-2xl font-medium w-4 left-4 leading-4 z-[999] absolute text-center text-white dark:text-white text-xxs top-5;
}
}
}
</style>
@@ -15,7 +15,7 @@
📄
</span>
</div>
<div class="max-w-3/5 min-w-[50%] overflow-hidden text-ellipsis">
<div class="max-w-[60%] min-w-[50%] overflow-hidden text-ellipsis">
<span
class="h-4 overflow-hidden text-sm font-medium text-ellipsis whitespace-nowrap"
>
@@ -1,6 +1,6 @@
<template>
<button
class="header-section flex items-center text-base font-normal mr-4 ml-2 p-0 cursor-pointer text-woot-500 dark:text-woot-500"
class="header-section flex items-center text-base font-normal mr-4 ml-2 cursor-pointer text-woot-500 dark:text-woot-500"
@click.capture="goBack"
>
<fluent-icon icon="chevron-left" />
@@ -2,7 +2,7 @@
<div class="empty-state py-16 px-1 ml-0 mr-0">
<h3
v-if="title"
class="text-slate-700 dark:text-slate-200 block text-center w-full text-xl font-thin"
class="text-slate-700 dark:text-slate-200 block text-center w-full text-4xl font-thin"
>
{{ title }}
</h3>
@@ -15,7 +15,6 @@
<slot />
</div>
</template>
<script>
export default {
props: {
@@ -1,6 +1,6 @@
<template>
<div
class="inbox--name inline-flex items-center py-0.5 px-0 leading-3 whitespace-nowrap font-medium bg-none text-slate-600 dark:text-slate-500 text-xs my-0 mx-2.5"
class="inbox--name inline-flex items-center py-0.5 px-0 leading-3 font-medium bg-none text-slate-600 dark:text-slate-500 text-xs my-0 mx-2.5"
>
<fluent-icon
class="mr-0.5 rtl:ml-0.5 rtl:mr-0"
@@ -1,11 +1,9 @@
<template>
<div class="flex items-center justify-center p-8">
<h6
class="flex items-center gap-2 text-base text-center w-100 text-slate-800 dark:text-slate-300"
class="block text-base text-center w-100 text-slate-800 dark:text-slate-300"
>
<span class="text-base font-medium text-slate-800 dark:text-slate-100">
{{ message }}
</span>
<span class="mr-3">{{ message }}</span>
<span class="spinner" />
</h6>
</div>
@@ -0,0 +1,57 @@
<template>
<div
class="small-2 report-card"
:class="{ active: selected }"
@click="onClick(index)"
>
<h3 class="heading">
<span>{{ heading }}</span>
<fluent-icon
v-if="infoText"
v-tooltip="infoText"
size="14"
icon="info"
class="info-icon"
/>
</h3>
<div class="metric-wrap">
<h4 class="metric">
{{ point }}
</h4>
<span v-if="trend !== 0" :class="trendClass">{{ trendValue }}</span>
</div>
<p class="desc">
{{ desc }}
</p>
</div>
</template>
<script>
export default {
props: {
heading: { type: String, default: '' },
infoText: { type: String, default: '' },
point: { type: [Number, String], default: '' },
trend: { type: Number, default: null },
index: { type: Number, default: null },
desc: { type: String, default: '' },
selected: Boolean,
onClick: { type: Function, default: () => {} },
},
computed: {
trendClass() {
if (this.trend > 0) {
return 'metric-trend metric-up';
}
return 'metric-trend metric-down';
},
trendValue() {
if (this.trend > 0) {
return `+${this.trend}%`;
}
return `${this.trend}%`;
},
},
};
</script>
@@ -2,7 +2,7 @@
<div
class="bg-slate-25 dark:bg-slate-900 pt-4 pb-0 px-8 border-b border-solid border-slate-50 dark:border-slate-800/50"
>
<h2 class="text-2xl text-slate-800 dark:text-slate-100 mb-1 font-medium">
<h2 class="text-2xl text-slate-800 dark:text-slate-100">
{{ headerTitle }}
</h2>
<p
@@ -1,7 +1,7 @@
<template>
<footer
v-if="isFooterVisible"
class="bg-white dark:bg-slate-800 h-12 border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between px-6"
class="bg-white dark:bg-slate-800 h-[60px] border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between py-0 px-4"
>
<div class="left-aligned-wrap">
<div class="text-xs text-slate-600 dark:text-slate-200">
@@ -98,7 +98,9 @@ export default {
},
computed: {
pageFooterIconClass() {
return this.isRTLView ? '-mr-3' : '-ml-3';
return this.isRTLView
? 'margin-right-minus-slab'
: 'margin-left-minus-slab';
},
isFooterVisible() {
return this.totalCount && !(this.firstIndex > this.totalCount);
@@ -9,7 +9,6 @@
<img
v-show="shouldShowImage"
:src="src"
draggable="false"
:class="thumbnailClass"
@load="onImgLoad"
@error="onImgError"
@@ -225,11 +225,8 @@ export default {
<style lang="scss">
.audio-wave-wrapper {
@apply h-20 min-h-[5rem];
.video-js {
@apply bg-transparent max-h-60 min-h-[3rem] pt-4 px-0 pb-0 resize-none;
}
min-height: 5rem;
height: 5rem;
}
.video-js .vjs-control-bar {
background-color: transparent;
@@ -104,7 +104,7 @@
class="fixed top-0 bottom-0 left-0 right-0 z-20 flex flex-col items-center justify-center w-full h-full gap-2 text-slate-900 dark:text-slate-50 bg-modal-backdrop-light dark:bg-modal-backdrop-dark"
>
<fluent-icon icon="cloud-backup" size="40" />
<h4 class="text-2xl break-words text-slate-900 dark:text-slate-50">
<h4 class="page-sub-title text-slate-900 dark:text-slate-50">
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
</h4>
</div>
@@ -146,6 +146,6 @@ export default {
}
}
.button--note {
@apply text-yellow-600 dark:text-yellow-600 bg-transparent dark:bg-transparent;
@apply text-yellow-600 dark:text-yellow-600;
}
</style>
@@ -8,7 +8,7 @@
:chat="currentChat"
:is-inbox-view="isInboxView"
:is-contact-panel-open="isContactPanelOpen"
:show-back-button="isOnExpandedLayout && !isInboxView"
:show-back-button="isOnExpandedLayout"
@contact-panel-toggle="onToggleContactPanel"
/>
<woot-tabs
@@ -35,10 +35,7 @@
:is-contact-panel-open="isContactPanelOpen"
@contact-panel-toggle="onToggleContactPanel"
/>
<empty-state
v-if="!currentChat.id && !isInboxView"
:is-on-expanded-layout="isOnExpandedLayout"
/>
<empty-state v-else :is-on-expanded-layout="isOnExpandedLayout" />
<div
v-show="showContactPanel"
class="conversation-sidebar-wrap basis-full sm:basis-[17.5rem] md:basis-[18.75rem] lg:basis-[19.375rem] xl:basis-[20.625rem] 2xl:basis-[25rem] rtl:border-r border-slate-50 dark:border-slate-700 h-auto overflow-auto z-10 flex-shrink-0 flex-grow-0"
@@ -2,8 +2,7 @@
<div
class="relative flex items-start flex-grow-0 flex-shrink-0 w-auto max-w-full px-4 py-0 border-t-0 border-b-0 border-l-2 border-r-0 border-transparent border-solid cursor-pointer conversation hover:bg-slate-25 dark:hover:bg-slate-800 group"
:class="{
'animate-card-select bg-slate-25 dark:bg-slate-800 border-woot-500':
isActiveChat,
'active bg-slate-25 dark:bg-slate-800 border-woot-500': isActiveChat,
'unread-chat': hasUnread,
'has-inbox-name': showInboxName,
'conversation-selected': selected,
@@ -26,12 +25,13 @@
v-if="bulkActionCheck"
:src="currentContact.thumbnail"
:badge="inboxBadge"
class="columns"
:username="currentContact.name"
:status="currentContact.availability_status"
size="40px"
/>
<div
class="px-0 py-3 border-b group-hover:border-transparent flex-1 border-slate-50 dark:border-slate-800/75 w-[calc(100%-40px)]"
class="px-0 py-3 border-b group-hover:border-transparent border-slate-50 dark:border-slate-800/75 columns"
>
<div class="flex justify-between">
<inbox-name v-if="showInboxName" :inbox="inbox" />
@@ -51,7 +51,7 @@
</div>
</div>
<h4
class="conversation--user text-sm my-0 mx-2 capitalize pt-0.5 text-ellipsis font-medium overflow-hidden whitespace-nowrap w-[calc(100%-70px)] text-slate-900 dark:text-slate-100"
class="conversation--user text-sm my-0 mx-2 capitalize pt-0.5 text-ellipsis overflow-hidden whitespace-nowrap w-[60%] text-slate-900 dark:text-slate-100"
>
{{ currentContact.name }}
</h4>
@@ -3,10 +3,11 @@
class="bg-white dark:bg-slate-900 flex justify-between items-center py-2 px-4 border-b border-slate-50 dark:border-slate-800/50 flex-col md:flex-row"
>
<div
class="flex-1 w-full min-w-0 flex flex-col items-center justify-center"
:class="isInboxView ? 'sm:flex-row' : 'md:flex-row'"
class="flex-1 w-full min-w-0 flex flex-col md:flex-row items-center justify-center"
>
<div class="flex justify-start items-center min-w-0 w-fit max-w-full">
<div
class="flex justify-start items-center mr-4 rtl:mr-0 rtl:ml-4 min-w-0 w-[inherit]"
>
<back-button
v-if="showBackButton"
:back-url="backButtonUrl"
@@ -19,11 +20,9 @@
:status="currentContact.availability_status"
/>
<div
class="items-start flex flex-col ml-2 rtl:ml-0 rtl:mr-2 min-w-0 w-fit overflow-hidden"
class="items-start flex flex-col ml-2 rtl:ml-0 rtl:mr-2 min-w-0 w-[inherit] overflow-hidden"
>
<div
class="flex items-center flex-row gap-1 m-0 p-0 w-fit max-w-full"
>
<div class="flex items-center flex-row gap-1 m-0 p-0 w-[inherit]">
<woot-button
variant="link"
color-scheme="secondary"
@@ -31,7 +30,7 @@
@click.prevent="$emit('contact-panel-toggle')"
>
<span
class="text-base leading-tight font-medium text-slate-900 dark:text-slate-100"
class="text-base leading-tight text-slate-900 dark:text-slate-100"
>
{{ currentContact.name }}
</span>
@@ -67,7 +66,7 @@
</div>
</div>
<div
class="header-actions-wrap items-center flex flex-row flex-grow justify-end mt-3 lg:mt-0"
class="header-actions-wrap items-center flex flex-row flex-grow justify-end mt-3 lg:mt-0 rtl:relative rtl:left-6"
:class="{ 'justify-end': isContactPanelOpen }"
>
<more-actions :conversation-id="currentChat.id" />
@@ -88,6 +87,7 @@ import Thumbnail from '../Thumbnail.vue';
import wootConstants from 'dashboard/constants/globals';
import { conversationListPageURL } from 'dashboard/helper/URLHelper';
import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
import { frontendURL } from 'dashboard/helper/URLHelper';
export default {
components: {
@@ -128,6 +128,9 @@ export default {
params: { accountId, inbox_id: inboxId, label, teamId },
name,
} = this.$route;
if (this.isInboxView) {
return frontendURL(`accounts/${accountId}/inbox`);
}
return conversationListPageURL({
accountId,
inboxId,
@@ -8,10 +8,7 @@
/>
<form class="w-full" @submit.prevent="onSubmit">
<div class="w-full">
<div
v-if="currentChat.meta.sender && currentChat.meta.sender.email"
class="flex items-center gap-2"
>
<div v-if="currentChat.meta.sender && currentChat.meta.sender.email">
<input
id="contact"
v-model="selectedType"
@@ -23,7 +20,7 @@
$t('EMAIL_TRANSCRIPT.FORM.SEND_TO_CONTACT')
}}</label>
</div>
<div v-if="currentChat.meta.assignee" class="flex items-center gap-2">
<div v-if="currentChat.meta.assignee">
<input
id="assignee"
v-model="selectedType"
@@ -35,7 +32,7 @@
$t('EMAIL_TRANSCRIPT.FORM.SEND_TO_AGENT')
}}</label>
</div>
<div class="flex items-center gap-2">
<div>
<input
id="other_email_address"
v-model="selectedType"
@@ -577,8 +577,7 @@ export default {
> img,
> video {
/** ensure that the bubble radius and image radius match*/
@apply rounded-[0.4rem];
@apply rounded-lg;
}
> video {
@@ -601,8 +600,8 @@ export default {
@apply text-woot-400 dark:text-woot-400;
}
.attachment-name {
@apply text-slate-700 dark:text-slate-200;
.text-block-title {
@apply text-slate-700 dark:text-slate-700;
}
.download.button {
@@ -76,16 +76,11 @@
class="conversation-footer"
:class="{ 'modal-mask': isPopoutReplyBox }"
>
<div
v-if="isAnyoneTyping"
class="items-center flex h-0 absolute w-full -top-7"
>
<div
class="flex py-2 pr-4 pl-5 shadow-md rounded-full bg-white dark:bg-slate-700 text-slate-800 dark:text-slate-100 text-xs font-semibold my-2.5 mx-auto"
>
<div v-if="isAnyoneTyping" class="typing-indicator-wrap">
<div class="typing-indicator">
{{ typingUserNames }}
<img
class="ltr:ml-2 rtl:mr-2 w-6"
class="gif"
src="~dashboard/assets/images/typing.gif"
alt="Someone is typing"
/>
@@ -552,8 +547,6 @@ export default {
<style scoped lang="scss">
.modal-mask {
@apply absolute;
&::v-deep {
.ProseMirror-woot-style {
@apply max-h-[25rem];
@@ -1,9 +1,9 @@
<template>
<div class="onboarding-wrap">
<div class="columns onboarding-wrap">
<div class="onboarding">
<div class="scroll-wrap">
<div class="features-item">
<h1 class="text-2xl text-slate-900 dark:text-slate-50">
<h1 class="page-title">
<span>{{
$t('ONBOARDING.TITLE', {
installationName: globalConfig.installationName,
@@ -30,7 +30,7 @@
</p>
</div>
<div class="features-item">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<h2 class="block-title text-black-900 dark:text-slate-200">
<span class="emoji">💬</span>
<span class="conversation--title">{{
$t('ONBOARDING.ALL_CONVERSATION.TITLE')
@@ -41,7 +41,7 @@
</p>
</div>
<div class="features-item">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<h2 class="block-title text-black-900 dark:text-slate-200">
<span class="emoji">👥</span>
{{ $t('ONBOARDING.TEAM_MEMBERS.TITLE') }}
</h2>
@@ -53,7 +53,7 @@
</router-link>
</div>
<div class="features-item">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<h2 class="block-title text-black-900 dark:text-slate-200">
<span class="emoji">📥</span>{{ $t('ONBOARDING.INBOXES.TITLE') }}
</h2>
<p class="intro-body">
@@ -64,7 +64,7 @@
</router-link>
</div>
<div class="features-item">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<h2 class="block-title text-black-900 dark:text-slate-200">
<span class="emoji">🔖</span>{{ $t('ONBOARDING.LABELS.TITLE') }}
</h2>
<p class="intro-body">
@@ -1202,28 +1202,10 @@ export default {
}
.reply-box {
transition:
box-shadow 0.35s cubic-bezier(0.37, 0, 0.63, 1),
height 2s cubic-bezier(0.37, 0, 0.63, 1);
@apply relative border-t border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900;
&.is-focused {
box-shadow:
0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
&.is-private {
@apply bg-yellow-100 dark:bg-yellow-800;
.reply-box__top {
@apply bg-yellow-100 dark:bg-yellow-800;
> input {
@apply bg-yellow-100 dark:bg-yellow-800;
}
}
@apply bg-yellow-50 dark:bg-yellow-200;
}
}
.send-button {
@@ -1232,10 +1214,6 @@ export default {
.reply-box__top {
@apply relative py-0 px-4 -mt-px border-t border-solid border-slate-50 dark:border-slate-700;
textarea {
@apply shadow-none border-transparent bg-transparent m-0 max-h-60 min-h-[3rem] pt-4 pb-0 px-0 resize-none;
}
}
.emoji-dialog {
@@ -5,11 +5,10 @@
<label class="input-group-label">
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.TO') }}
</label>
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
<div class="input-group-field">
<woot-input
v-model.trim="$v.toEmailsVal.$model"
type="text"
class="[&>input]:mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
:class="{ error: $v.toEmailsVal.$error }"
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
@blur="onBlur"
@@ -22,10 +21,9 @@
<label class="input-group-label">
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.LABEL') }}
</label>
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
<div class="input-group-field">
<woot-input
v-model.trim="$v.ccEmailsVal.$model"
class="[&>input]:mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
type="text"
:class="{ error: $v.ccEmailsVal.$error }"
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
@@ -50,11 +48,10 @@
<label class="input-group-label">
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.BCC.LABEL') }}
</label>
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
<div class="input-group-field">
<woot-input
v-model.trim="$v.bccEmailsVal.$model"
type="text"
class="[&>input]:mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
:class="{ error: $v.bccEmailsVal.$error }"
:placeholder="
$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.BCC.PLACEHOLDER')
@@ -153,11 +150,14 @@ export default {
@apply text-sm text-red-500 dark:text-red-500;
}
.input-group {
@apply border-b border-solid border-slate-75 dark:border-slate-700 my-1 flex items-center gap-2;
@apply border-b border-solid border-slate-75 dark:border-slate-700 my-1;
.input-group-label {
@apply border-transparent bg-transparent text-xs font-semibold pl-0;
}
.input-group-field::v-deep input {
@apply mb-0 border-transparent;
}
}
.input-group.error {
@@ -1,6 +1,6 @@
<template>
<div class="w-full">
<div class="templates__list-search gap-1">
<div class="templates__list-search">
<fluent-icon icon="search" class="search-icon" size="16" />
<input
ref="search"
@@ -17,7 +17,7 @@
@click="$emit('onSelect', template)"
>
<div>
<div class="flex items-center justify-between mb-2.5">
<div class="flex-between">
<p class="label-title">
{{ template.name }}
</p>
@@ -97,6 +97,10 @@ export default {
</script>
<style scoped lang="scss">
.flex-between {
@apply flex justify-between mb-2.5;
}
.templates__list-search {
@apply items-center flex bg-slate-25 dark:bg-slate-900 mb-2.5 py-0 px-2.5 rounded-md border border-solid border-slate-100 dark:border-slate-700;
@@ -105,7 +109,7 @@ export default {
}
.templates__search-input {
@apply bg-transparent border-0 text-xs h-9 m-0;
@apply bg-transparent border-0 text-xs h-auto m-0;
}
}
.template__list-container {

Some files were not shown because too many files have changed in this diff Show More