lot of updates

This commit is contained in:
Harry Bomrah 2015-08-25 18:49:47 +08:00
parent 5e3a521df1
commit 8c9f3125f7
15 changed files with 478 additions and 108 deletions

View File

@ -2,4 +2,73 @@
#filter {
display: none;
}
.img-avatar {
width: 50px;
height: 50px;
}
.t-avatar {
width: 30px;
height: 30px;
border-radius: 50%;
}
.t-label {
border-radius: 0.125rem;
display: inline-block;
padding: 4px 0.5rem;
font-family: $main-font;
}
.t-label-primary {
color: $gray;
background-color: $white;
}
.t-status {
text-transform: uppercase;
border-radius: 0.125rem;
font-size: 0.75rem;
display: inline-block;
padding: 4px 0.375rem;
font-family: $main-font;
}
.t-status-open {
color: $white;
background-color: $green;
}
.t-status-close {
color: $white;
background-color: $gray;
}
.t-status-processing {
color: $white;
background-color: $purple;
}
// change bootstrap label colors to fit this design theme
.label {
text-shadow: none;
font-family: $main-font;
font-size: 12px;
font-weight: 100;
display: inline-block;
}
.label-primary {
background-color: $primary;
}
.label-open {
background-color: $green;
}
.label-commenced {
background-color: $purple;
}
.label-danger {
background-color: $red;
}

View File

@ -2,6 +2,7 @@
@import 'ticket_variables';
@import 'common';
@import 'classes';
body {
background-color: $subtle-gray;
}
@ -22,14 +23,28 @@ body {
margin: auto;
}
.ticket-search-option {
width: 10%;
}
input.ticket-search-field {
width: 80%;
height: 30px;
margin-bottom: 0;
.ticket-search-wrap {
font-family: $main-font;
color: $gray;
.ticket-search-option {
width: 15%;
height: 50px;
border: none;
border-radius: $t-round;
font-size: 18px;
}
.ticket-search-field {
color: $gray;
font-size: 18px;
font-weight: normal;
padding: 0 20px;
width: 84%;
height: 50px;
margin-bottom: 0;
box-shadow: none;
border: none;
border-radius: $t-round;
}
}
.ticket-section {
@ -79,6 +94,19 @@ input.ticket-search-field {
&:hover {
background-color: $light-blue;
}
.ticket-site-link {
margin-left: 8px;
padding: 2px 4px;
font-size: 12px;
color: $white;
background-color: $second;
border-radius: $t-strong-round;
&:hover {
color: $white;
text-decoration: none;
background-color: darken($second, 10%);
}
}
}
.ticket-item:first-child {
@ -117,3 +145,15 @@ input.ticket-search-field {
-webkit-webkit-transition: $t-fast all;
transition: $t-fast all;
}
// Search result
.ticket-search-result {
background-color: $second;
padding: 16px 14px;
color: $white;
font-size: 24px;
a {
color: $white;
}
}

View File

@ -2,7 +2,6 @@
@import 'ticket_variables';
@import 'common';
@import 'classes';
#main-wrap .wrap-inner {
padding-top: 20px !important;
}
@ -110,50 +109,12 @@
}
}
.t-list-unstyled {
list-style: none;
margin: 0;
padding: 0;
}
.t-label {
border-radius: 0.125rem;
display: inline-block;
padding: 4px 0.5rem;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
.t-label-primary {
color: $gray;
background-color: $white;
}
.t-status {
text-transform: uppercase;
border-radius: 0.125rem;
font-size: 0.75rem;
display: inline-block;
padding: 4px 0.375rem;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
.t-status-open {
color: $white;
background-color: $green;
}
.t-status-close {
color: $white;
background-color: $gray;
}
.t-status-processing {
color: $white;
background-color: $purple;
}
.t-category {
border-radius: 0.125rem;
display: inline-block;
@ -221,7 +182,6 @@
transition: .15s all ease-in-out;
-webkit-box-shadow: 0 2px 15px 0 rgba(0, 0, 0, .1);
margin: 0;
}
.notice-pop {
@ -249,6 +209,7 @@
padding-top: 70px;
}
}
.notice-bar {
position: fixed;
left: 61px;
@ -262,10 +223,6 @@
}
}
.notice-bar-close {
}
.ticket-wrap {
position: relative;
max-width: 1000px;
@ -307,6 +264,9 @@
}
.ticket-state {
float: right;
> * {
// vertical-align: top;
}
}
.ticket-urgent-btn {
margin-right: 3px;
@ -325,6 +285,85 @@
}
}
.onoffswitch {
position: relative;
width: 70px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
display: inline-block;
}
.onoffswitch-checkbox {
display: none;
}
.onoffswitch-label {
display: block;
overflow: hidden;
cursor: pointer;
border-radius: 20px;
margin-bottom: 0;
}
.onoffswitch-inner {
display: block;
width: 200%;
margin-left: -100%;
transition: margin .15s ease-in 0s;
}
.onoffswitch-inner:before,
.onoffswitch-inner:after {
display: block;
float: left;
width: 50%;
height: 30px;
padding: 0;
line-height: 30px;
font-size: 12px;
color: white;
font-family: $main-font;
box-sizing: border-box;
}
.onoffswitch-inner:before {
content: "ON";
padding-left: 10px;
background-color: $red;
color: $white;
}
.onoffswitch-inner:after {
content: "OFF";
padding-right: 10px;
background-color: $gray;
color: $white;
text-align: right;
}
.onoffswitch-switch {
display: block;
width: 18px;
height: 18px;
margin: 6px;
background: $white;
position: absolute;
top: 0;
bottom: 0;
right: 40px;
border-radius: 50%;
transition: all .15s ease-in 0s;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
right: 0px;
}
.ticket-action {
position: absolute;
right: 1.4rem;
@ -387,8 +426,9 @@
}
.ticket-category {
position: relative;
position: relative;
}
.ticket-category-dropdown {
text-transform: uppercase;
color: $white;
@ -498,12 +538,14 @@
.ticket-response-heading-icon {
margin-right: 0.5rem;
}
.ticket-response-wrap {
&.collapsed {
height: 0;
overflow: hidden;
}
}
.ticket-response-item {
margin: 35px auto;
width: 90%;
@ -545,6 +587,83 @@
overflow: auto;
}
.ticket-inhouse-heading {
display: block;
font-size: 12px;
}
// Override select2 styles, I need this evil power
#main-wrap {
.select2-container-multi {
margin-right: 0.9375rem;
min-width: 200px;
.select2-choices {
padding: 0;
border-radius: $t-round;
.select2-search-field {
input {
padding: 10px 5px;
}
}
}
.select2-search-choice {
padding: 10px 1.75rem 10px 0.7rem;
border-color: lighten($gray, 20%);
background: $white;
> div {
&:before {
content: "\F007";
font-family: FontAwesome;
display: inline-block;
font-size: 0.9375rem;
color: $second;
margin: 0 0.5rem 0 0;
}
}
}
.select2-search-choice-close {
right: 15px;
left: auto;
&:before {
content: "\f057";
font-family: FontAwesome;
position: absolute;
cursor: pointer;
left: 6px;
top: 6px;
font-size: 0.9375rem;
color: $gray;
}
&:hover {
&:before {
color: $red;
}
}
}
}
}
.select2-result-label {
> span {
white-space: nowrap;
}
}
.fileupload {
.thumbnail {
max-width: 60%;
height: auto;
}
}
// editor
.ticket-editor {
padding: 10px 1.8rem 20px;
@ -572,7 +691,6 @@
}
}
// ticket table
.ticket-table {
td {
@ -593,32 +711,8 @@
}
}
// .ticket-category-select{
// border: medium none;
// text-decoration: none;
// color: $white;
// border-radius: 0.125rem;
// left: auto;
// min-width: 100px;
// padding: 15px 0;
// right: 0;
// top: 8.3%;
// }
// .ticket-category-dropdown{
// color: $white;
// text-decoration: none;
// &:hover{
// color : $white;
// text-decoration: none;
// }
// &:active{
// outline:none;
// }
// &:focus{
// outline:none;
// }
// }
// till here
.scroll-element {
border: 5px solid #489ad8;
-webkit-transition: border-color 1s linear;
transition: border-color 1s linear;
}

View File

@ -110,7 +110,7 @@ class Admin::TicketsApiController < ApplicationController
response["ticket"]["queries"] = []
ticket.ticket_queries.asc(:created_at).each do |qu|
r = {"query" => qu.query, "responses" => []}
r["responses"] = qu.ticket_query_responses.asc(:created_at).collect do |qr|
r["responses"] = qu.ticket_query_responses.for_clients.asc(:created_at).collect do |qr|
user = User.find(qr.response_by) rescue nil
if user.member_profile.avatar.nil? || user.member_profile.avatar.thumb.url == "thumb_person.png"
avatar = "http://" + request.host_with_port + "/assets/thumb_person.png"

View File

@ -41,6 +41,19 @@ class Admin::TicketsController < OrbitAdminController
def show
@ticket = Ticket.find(params[:id]) rescue nil
@categories = ModuleApp.find_by_key("ticket").categories
@members = []
MemberProfile.all.each do |mp|
user = mp.user rescue nil
if !user.nil? && user.id.to_s != current_user.id.to_s && user.user_name != "rulingcom"
avatar = (mp.avatar.thumb.url == "thumb_person.png" ? "/assets/thumb_person.png" : mp.avatar.thumb.url rescue "/assets/thumb_person.png")
@members << {
"id" => user.id.to_s,
"user_name" => (user.user_name rescue ""),
"avatar" => avatar,
"name" => (mp.name_translations rescue {"en" => "","zh_tw" => ""})
}
end
end
end
def changecategory
@ -77,6 +90,27 @@ class Admin::TicketsController < OrbitAdminController
redirect_to admin_ticket_path(ticket.id)
end
def reopen
ticket = Ticket.find(params[:ticket_id]) rescue nil
ticket.status = "open"
ticket.reopened_count = ticket.reopened_count + 1
tq = TicketQuery.new
tq.query = params[:ticket_query]
tq.ticket = ticket
tq.save
ticket.save
response = {}
response["success"] = true
render :json => response.to_json
end
def destroy
tick = Ticket.find(params[:id]) rescue nil
tick.destroy if !tick.nil?
redirect_to admin_tickets_path
end
def toggleurgent
mark_urgent = params[:mark_urgent]
if mark_urgent
@ -148,7 +182,13 @@ class Admin::TicketsController < OrbitAdminController
response = TicketQueryResponse.new
response.response = smart_store_link_parser(params[:ticket_query_response])
response.response_by = current_user.id
response.internal_response = params[:internal_response] if params[:internal_response].present?
response.user_tags = params[:user_tags] if params[:user_tags].present?
response.save
if params[:user_tags].present?
p1 = fork { send_notification_emails(response, tq.ticket) }
Process.detach(p1)
end
tq.ticket_query_responses << response
render :partial => "response", :object => response
else

View File

@ -10,4 +10,20 @@ module TicketsHelper
html = html.gsub("src=\"/uploads/","src=\"http://#{request.host_with_port}/uploads/")
return html
end
def send_notification_emails(response, ticket)
response.user_tags.each do |u|
user = User.find(u) rescue nil
if !user.nil?
mp = user.member_profile
send_email(mp.email,ticket,response,user.name) if !mp.email.nil?
end
end
end
def send_email(useremail,ticket,response, username)
url = "#{request.protocol}#{request.host_with_port}/#{I18n.locale}/admin/tickets/#{ticket.id.to_s}#response_#{response.id.to_s}"
email = Email.new(:mail_to => useremail, :mail_subject => "Tagged : #{ticket.subject}.", :template => "email/ticket_tag_email.html.erb", :template_data => {"url" => url, "mention" => current_user.name, "name" => username})
email.deliver
end
end

View File

@ -3,7 +3,11 @@ class TicketQueryResponse
include Mongoid::Timestamps
field :response
field :internal_response, type: Boolean, :default => false
field :response_by, type: BSON::ObjectId
field :user_tags, type: Array, default: []
scope :for_clients, ->{ where(:internal_response.in => [nil, false]) }
belongs_to :ticket_query
end

View File

@ -15,6 +15,22 @@
<form method="post" action="/admin/ticket/submit_response" id="reopen-form" for="query-response">
<textarea class="ckeditor" name="ticket_query_response" data-fv-validation="required;" data-fv-messages="Cannot be empty;"></textarea>
<input type="hidden" name="ticket_query_id" value="<%= query.id.to_s %>" />
<input type="checkbox" name="internal_response" value="1">
<span class="ticket-internal-response">Internal Response</span>
<div class="ticket-tag-member">
<h4 class="ticket-tag-mebmer-heading">Tag member</h4>
<select class="member_tags" multiple="multiple" name="user_tags[]">
<% @members.each do |member| %>
<%
name = member["name"][I18n.locale.to_s] == "" ? member["name"]["zh_tw"] : member["name"][I18n.locale.to_s]
name = "" if name.nil?
user_name = "(#{member["user_name"]})"
text = " #{name} #{user_name}"
%>
<option value="<%= member["id"] %>" data-member="<%= member.to_json %>"><%= text %></option>
<% end %>
</select>
</div>
</form>
<input type="submit" id="reopen-form-submit" class="btn btn-primary" value="Submit" />
</div>

View File

@ -1,4 +1,4 @@
<div class="ticket-response-item">
<div class="ticket-response-item" data-scroll-id="response_<%= response.id.to_s %>">
<div class="ticket-response-meta">
<div class="ticket-response-author">
<% user = User.find(response.response_by) rescue nil %>
@ -14,6 +14,22 @@
<div class="ticket-response-created-date">
<% dt = DateTime.parse(response.created_at.to_s) %> <%= dt.strftime("%d %B %Y - %H:%M") %>
</div>
<h4 class="ticket-inhouse-heading">
<%= response.internal_response ? "Inhouse" : "" %>
</h4>
<div class="ticket-inhouse-wrap">
<% response.user_tags.each do |u|
user = User.find(u) rescue nil
if !user.nil?
if user.member_profile.avatar.nil? || user.member_profile.avatar.thumb.url == "thumb_person.png"
avatar = "/assets/thumb_person.png"
else
avatar = user.member_profile.avatar.thumb.url
end
end %>
<img class="t-avatar" src="<%= avatar %>">
<% end %>
</div>
</div>
<div class="ticket-response-content">
<%= response.response.html_safe %>

View File

@ -1,18 +1,25 @@
<div class="search-wrap ticket-search-wrap">
<!-- <a href="#" id="serach-btn"><i class="icons-search"></i></a> -->
<% if params[:keywords].present? %>
<a href="/admin/tickets"><i class="icons-cycle"></i></a>
<span>Searched for <b><i><%= params[:keywords] %></i></b></span>
<% end %>
<h3 class="ticket-search-heading">
<i class="ticket-search-icon fa fa-search"></i>
Search tickets
</h3>
<span class="search-box">
<form action="/admin/tickets/search" method="get" id="search-form">
<select id="smart-field-select" class="ticket-search-option">
<option value="">general</option>
<option value="">General</option>
<% @smart_search_fields.each do |ssf| %>
<option value="<%= ssf %>"><%= ssf %></option>
<option value="<%= ssf %>"><%= ssf.capitalize %></option>
<% end %>
</select>
<input class="ticket-search-field" type="text" placeholder="Search" name="keywords" />
<input class="ticket-search-field" type="text" placeholder="keyword" name="keywords">
</form>
</span>
<% if params[:keywords].present? %>
<div class="ticket-search-result">
<a href="/admin/tickets"><i class="icons-cycle"></i></a>
Search result for <b><i>"<%= params[:keywords] %>"</i></b>
</div>
<% end %>
</div>

View File

@ -9,17 +9,24 @@
end
%>
<div class="ticket ticket-item" id="ticket_<%= ticket.id.to_s %>" data-id="<%= ticket.id.to_s %>">
#<%= ticket.uid %> - <a class="ticket-link" href="<%= admin_ticket_path(ticket.id) %>"><span class="ticket-title"><%= ticket.subject %></span> </a><a href="http://<%= ticket.registered_site.site_domain %>" target="_blank">(<%= ticket.registered_site.title %>)</a>
#<%= ticket.uid %> -
<a class="ticket-link" href="<%= admin_ticket_path(ticket.id) %>">
<span class="ticket-title"><%= ticket.subject %></span>
</a>
<a class="ticket-site-link" href="http://<%= ticket.registered_site.site_domain %>" target="_blank">
<i class="fa fa-external-link"></i>
<%= ticket.registered_site.title %>
</a>
<div class="ticket-label pull-right">
<% if ticket.is_urgent? %>
<span class="label label-danger">Urgent</span>
<% end %>
<span class="label label-info <%= badge_class %>">
<span class="label <%= badge_class %>">
<i class="fa fa-eye"></i>
<%= ticket.status %>
<%= ticket.status.capitalize %>
</span>
<% if !ticket.taken_by.nil? %>
<span class="ticket-author t-label t-label-primary">
<span class="ticket-author label label-primary">
<i class="icon-user"></i>
<% user = User.find(ticket.taken_by) %>
<%= user.name if !user.nil? %>

View File

@ -1,15 +1,24 @@
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "tickets" %>
<%= stylesheet_link_tag "ticket" %>
<%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" %>
<%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" %>
<%= stylesheet_link_tag "tickets/ticket_index" %>
<% end %>
<%= render :partial => "search_form" %>
<div>
<div class="ticket-container">
<% @categories.each do |category| %>
<div>
<h3><%= category.title %></h3>
<div class="ticket-section-container">
<h4 class="ticket-category-title">
<%= category.title %>
</h4>
<%= render :partial => "ticket", :collection => @tickets[category] %>
</div>
<div class="pull-right"><a href="/admin/tickets/category/<%= category.id.to_s %>/?<%= request.original_fullpath.split("?")[1] %>">View all tickets from <%= category.title %></a></div>
<div class="ticket-section-view-all">
<a class="ticket-section-view-all-btn" href="/admin/tickets/category/<%= category.id.to_s %>/?<%= request.original_fullpath.split("?")[1] %>">
<i class="ticket-section-view-all-icon fa fa-share"></i>
View all tickets from <%= category.title %>
</a>
</div>
</div>
<% end %>
</div>
<script type="text/javascript" src="/assets/search.js"></script>

View File

@ -1,10 +1,12 @@
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" %>
<%= stylesheet_link_tag "tickets/ticket_show" %>
<%= stylesheet_link_tag "select2/select2" %>
<% end %>
<% content_for :page_specific_javascript do %>
<%= javascript_include_tag "lib/jquery.form.js" %>
<%= javascript_include_tag "validator" %>
<%= javascript_include_tag "select2/select2.min" %>
<% end %>
<% if !@ticket .nil? %>
<% case @ticket.status
@ -76,6 +78,12 @@
</li>
<% end %>
<% end %>
<li>
<a class="ticket-close t-item" data-method="delete" data-confirm="Are you sure?" href="/admin/tickets/<%= @ticket.id.to_s %>" data-ticket-id="<%= @ticket.id.to_s %>">
<i class="fa fa-trash-o"></i>
Delete
</a>
</li>
</ul>
</div>
</div>
@ -96,7 +104,14 @@
<a href="/admin/tickets/<%= @ticket.id.to_s %>/toggleurgent" class="ticket-urgent-btn t-btn t-btn-danger t-uppercase <%= "active" if @ticket.is_urgent? %>" id="toggle-urgent">
<i class="fa fa-fire"></i>
Urgent
</a>
<!-- <div class="onoffswitch">
<input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox" id="myonoffswitch">
<label class="onoffswitch-label" for="myonoffswitch">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
--> </a>
<% end %>
<span class="ticket-status t-status <%= badge_class %>">
<i class="fa fa-eye"></i>
@ -128,9 +143,8 @@
<% if params[:reopen] == "true"%>
<% if @ticket.status == "closed" %>
<div id="form_container" class="ticket-editor">
<form method="post" action="/admin/ticket/reopen" id="reopen-form" for="query">
<form method="post" action="/admin/tickets/<%= @ticket.id.to_s %>/reopen" id="reopen-form" for="query">
<textarea class="ckeditor" name="ticket_query" data-fv-validation="required;" data-fv-messages="Cannot be empty;"></textarea>
<input type="hidden" name="ticket_id" value="<%= @ticket.uid %>" />
</form>
<input type="submit" id="reopen-form-submit" class="btn btn-primary" value="Reopen" />
<input type="button" class="btn reopen-back-btn" value="Cancel" />
@ -150,6 +164,7 @@
</div>
<% end %>
<script type="text/javascript">
var locale = "<%= I18n.locale.to_s %>";
var fv = new FormValidator($("#reopen-form"));
$("#reopen-form-submit").on("click",function(){
for (instance in CKEDITOR.instances) {
@ -166,6 +181,7 @@
window.location.href = window.location.pathname;
}else{
fv.reset();
$(".member_tags").select2("val","");
for (instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].setData("");
}
@ -174,10 +190,42 @@
}
$("#ticket_loader").hide();
},500)
},
error : function(){
$("#ticket_loader").find("img").hide();
$("#ticket_loader").find("div").text("There was some error.");
}
});
}
})
$(document).ready(function(){
if(window.location.hash != ""){
var item = window.location.hash.replace("#","");
el = $("div[data-scroll-id=" + item + "]");
$('html, body').scrollTop(el.offset().top - 100);
el.addClass("scroll-element");
el.one("mouseover",function(){
el.css( "border-color", "rgba(0,0,0,0)");
})
}
$(".member_tags").select2({
formatResult: function (el) {
var $el = $(el.element),
member = $el.data("member"),
name = (member.name[locale] == "" ? member.name.zh_tw : member.name[locale]),
user_name = "(" + member.user_name + ")",
text = name + " " + user_name;
if (!el.id) { return el.text; }
var $state = $(
'<span><img src="' + member.avatar + '" class="img-avatar" /> ' + text + '</span>'
);
return $state;
},
placeholder: "Add Members"
});
})
$(".reopen-back-btn").on("click",function(){
for (instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
@ -199,8 +247,8 @@
return false;
})
$("#toggle-urgent").on("click",function(){
var el = $(this),
mark_urgent = !el.hasClass("active");
var el = $(this);
var mark_urgent = !el.hasClass("active");
$.ajax({
url : el.attr("href"),
type : "post",

View File

@ -0,0 +1,3 @@
<h3>Hello <%= @data["name"] %>,</h3>
<p><%= @data["mention"] %> mentioned you in a ticket.
<a href="<%= @data['url'] %>" > Please click here to view the ticket.</a>

View File

@ -21,6 +21,7 @@ Rails.application.routes.draw do
get "changecategory"
get "close"
post "toggleurgent"
post "reopen"
end
end
end