support_center 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e5e17d22cf4cb911fce90aacc0191e7a822dbce3
4
+ data.tar.gz: a0d4ad2f3c277389eb14566a415b328560439959
5
+ SHA512:
6
+ metadata.gz: cf6a9fff66b0b5d7a84b5d2ced0800246b1571bd0d80deb2392755e274384e7bafba147ee5212e997d1c950f6478d8d1b82d4b0ed704c72cbfe15122210502c8
7
+ data.tar.gz: 61e438dfe61b9ad7be1d68eb4ca02ad84ba4072c2759dd4bfccfe644799f80e7e32b0754bfc9cfcbf6df57d841e2f67e6280963412e44806bd71090fea9a75c7
@@ -0,0 +1,14 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # bundler
9
+ .bundle
10
+
11
+ .DS_Store
12
+ .tags
13
+
14
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -fn
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rake'
7
+ gem 'rspec'
8
+ gem 'rack-test'
9
+ gem 'pry'
10
+ gem 'pry-debugger'
11
+ gem 'jasmine', '~> 1.3.0'
12
+ end
13
+
14
+ group :test do
15
+ gem "simplecov", require: false
16
+ end
@@ -0,0 +1,101 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ support_center (0.1.5)
5
+ sinatra (>= 1.2.0)
6
+ slim
7
+ zendesk_api (~> 1.3)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ childprocess (0.5.3)
13
+ ffi (~> 1.0, >= 1.0.11)
14
+ coderay (1.1.0)
15
+ columnize (0.8.9)
16
+ debugger (1.6.8)
17
+ columnize (>= 0.3.1)
18
+ debugger-linecache (~> 1.2.0)
19
+ debugger-ruby_core_source (~> 1.3.5)
20
+ debugger-linecache (1.2.0)
21
+ debugger-ruby_core_source (1.3.5)
22
+ diff-lcs (1.2.5)
23
+ docile (1.1.3)
24
+ faraday (0.9.0)
25
+ multipart-post (>= 1.2, < 3)
26
+ ffi (1.9.3)
27
+ hashie (3.0.0)
28
+ inflection (1.0.0)
29
+ jasmine (1.3.2)
30
+ jasmine-core (~> 1.3.1)
31
+ rack (~> 1.0)
32
+ rspec (>= 1.3.1)
33
+ selenium-webdriver (>= 0.1.3)
34
+ jasmine-core (1.3.1)
35
+ method_source (0.8.2)
36
+ mime-types (1.25.1)
37
+ multi_json (1.10.1)
38
+ multipart-post (2.0.0)
39
+ pry (0.9.12.6)
40
+ coderay (~> 1.0)
41
+ method_source (~> 0.8)
42
+ slop (~> 3.4)
43
+ pry-debugger (0.2.2)
44
+ debugger (~> 1.3)
45
+ pry (~> 0.9.10)
46
+ rack (1.5.2)
47
+ rack-protection (1.5.3)
48
+ rack
49
+ rack-test (0.6.2)
50
+ rack (>= 1.0)
51
+ rake (10.3.2)
52
+ rspec (2.14.1)
53
+ rspec-core (~> 2.14.0)
54
+ rspec-expectations (~> 2.14.0)
55
+ rspec-mocks (~> 2.14.0)
56
+ rspec-core (2.14.8)
57
+ rspec-expectations (2.14.5)
58
+ diff-lcs (>= 1.1.3, < 2.0)
59
+ rspec-mocks (2.14.6)
60
+ rubyzip (1.1.4)
61
+ selenium-webdriver (2.42.0)
62
+ childprocess (>= 0.5.0)
63
+ multi_json (~> 1.0)
64
+ rubyzip (~> 1.0)
65
+ websocket (~> 1.0.4)
66
+ simplecov (0.8.2)
67
+ docile (~> 1.1.0)
68
+ multi_json
69
+ simplecov-html (~> 0.8.0)
70
+ simplecov-html (0.8.0)
71
+ sinatra (1.4.5)
72
+ rack (~> 1.4)
73
+ rack-protection (~> 1.4)
74
+ tilt (~> 1.3, >= 1.3.4)
75
+ slim (1.3.9)
76
+ temple (~> 0.6.3)
77
+ tilt (~> 1.3, >= 1.3.3)
78
+ slop (3.5.0)
79
+ temple (0.6.7)
80
+ tilt (1.4.1)
81
+ websocket (1.0.7)
82
+ zendesk_api (1.3.7)
83
+ faraday (~> 0.9)
84
+ hashie (>= 1.2)
85
+ inflection
86
+ mime-types (~> 1.0)
87
+ multi_json
88
+ multipart-post (~> 2.0)
89
+
90
+ PLATFORMS
91
+ ruby
92
+
93
+ DEPENDENCIES
94
+ jasmine (~> 1.3.0)
95
+ pry
96
+ pry-debugger
97
+ rack-test
98
+ rake
99
+ rspec
100
+ simplecov
101
+ support_center!
@@ -0,0 +1,3 @@
1
+ src: coffee -cw -o js/support_center js/support_center/src
2
+ spec: coffee -cw -o spec/javascripts spec/coffee
3
+ jasmine: rake jasmine
@@ -0,0 +1,30 @@
1
+ Support Center
2
+ --------------
3
+
4
+ Rack Middleware for interacting with the Zendesk API
5
+
6
+ Basic Usage
7
+ -----------
8
+
9
+ Support Center requires a config block in the following format:
10
+
11
+ ```ruby
12
+ require 'support_center'
13
+
14
+ SupportCenter.setup do |config|
15
+ config.environment = ENV['RACK_ENV'] # optional
16
+ config.config_service_adapter = {username: 'foo@bar.com', password: 'p@ssw0rd12345', url: 'https://foo.zendesk.com/api/v2'}
17
+ end
18
+ ```
19
+
20
+ Then add the following in your config.ru:
21
+ ```ruby
22
+ run Rack::URLMap.new('/' => SupportCenter.new)
23
+ ```
24
+
25
+ If need DCMS, mount the app as Rack::Middleware after DCMS is setup:
26
+ ```ruby
27
+ # e.g. config/application.rb
28
+ require 'support_center/server'
29
+ use SupportCenter::Server
30
+ ```
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+ require 'bundler/gem_tasks'
14
+ require 'rspec/core/rake_task'
15
+
16
+ RSpec::Core::RakeTask.new(:spec) do |t|
17
+ t.pattern = "spec/support_center/**/*_spec.rb"
18
+ end
19
+
20
+ begin
21
+ require 'yaml'
22
+ require 'jasmine'
23
+ load 'jasmine/tasks/jasmine.rake'
24
+ rescue LoadError
25
+ task :jasmine do
26
+ abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
27
+ end
28
+ end
29
+
30
+ task default: :spec
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "support_center",
3
+ "version": "0.1.4",
4
+ "main": "js/support_center/support_center.js",
5
+ "dependencies": {
6
+ "jquery": null,
7
+ "flight": ">=1.0.4 < 1.1.0",
8
+ "js/support_center/form_component"
9
+ },
10
+ "devDependencies": {
11
+ "requirejs": null
12
+ },
13
+ "ignore": [
14
+ "Gemfile",
15
+ "Procfile",
16
+ "Rakefile",
17
+ "js/support_center/src/",
18
+ "package.json",
19
+ "vendor",
20
+ "Gemfile.lock",
21
+ "README.md",
22
+ "lib",
23
+ "spec",
24
+ "support_center.gemspec"
25
+ ]
26
+ }
@@ -0,0 +1,161 @@
1
+ // Generated by CoffeeScript 1.7.1
2
+ (function() {
3
+ define(['jquery', 'flight/lib/component'], function($, defineComponent) {
4
+ var FormComponent;
5
+ FormComponent = function() {
6
+ this.defaultAttrs({
7
+ ticketSubmitRoute: '/support/tickets/',
8
+ spinnerClass: '.spinner',
9
+ questionForm: '#questions_form_element',
10
+ questionFormThanks: '#thanks_questions_tab',
11
+ questionButton: '#questions_form',
12
+ questionFormContainer: '#questions_form_tab',
13
+ questionBrowserInput: '#question_browser',
14
+ questioncurrentURL: '#question_current_url',
15
+ feedbackForm: '#feedback_form_element',
16
+ feedbackFormThanks: '#thanks_feedback_tab',
17
+ feedbackButton: '#feedback_form',
18
+ feedbackFormContainer: '#feedback_form_tab',
19
+ feedbackBrowserInput: '#feedback_browser',
20
+ feedbackcurrentURL: '#feedback_current_url'
21
+ });
22
+ this.handleFormSubmit = function(ev, formType) {
23
+ this.disableForm(formType);
24
+ this.populateMetadata(formType);
25
+ $.ajax({
26
+ url: this.attr.ticketSubmitRoute,
27
+ type: "POST",
28
+ data: this.select("" + formType + "Form").serialize(),
29
+ dataType: "json",
30
+ beforeSend: (function(_this) {
31
+ return function(xhr, opts) {
32
+ _this.showSpinner();
33
+ _this.clearErrors(formType);
34
+ return _this.checkFormErrors(formType, xhr);
35
+ };
36
+ })(this),
37
+ success: (function(_this) {
38
+ return function(response, textStatus) {
39
+ if (response.ticket) {
40
+ _this.displayThankYou(formType);
41
+ _this.clearForm(formType);
42
+ return _this.trigger('supportTicketSubmitted');
43
+ }
44
+ };
45
+ })(this),
46
+ error: (function(_this) {
47
+ return function(jqXHR, textStatus) {
48
+ _this.displayFormError(formType, 'Failed to submit ticket. Please try again later.');
49
+ _this.disableForm(formType, false);
50
+ return _this.hideSpinner();
51
+ };
52
+ })(this)
53
+ });
54
+ if (ev.preventDefault) {
55
+ return ev.preventDefault();
56
+ } else {
57
+ return ev.returnValue = false;
58
+ }
59
+ };
60
+ this.populateMetadata = function(formType) {
61
+ var browserInfo, currentURL;
62
+ browserInfo = navigator.userAgent;
63
+ currentURL = window.location.href;
64
+ this.select("" + formType + "BrowserInput").val(browserInfo);
65
+ return this.select("" + formType + "currentURL").val(currentURL);
66
+ };
67
+ this.checkFormErrors = function(formType, xhr) {
68
+ var blank_inputs, email;
69
+ blank_inputs = this.select("" + formType + "Form").find("input:text[value=''], textarea[value='']");
70
+ if (blank_inputs.length > 0) {
71
+ $(blank_inputs).addClass('error_input');
72
+ this.displayFormError(formType, 'Missing Required Field');
73
+ this.disableForm(formType, false);
74
+ this.hideSpinner();
75
+ xhr.abort();
76
+ return;
77
+ }
78
+ email = this.select("" + formType + "Form").find("input[name='ticket[requester][email]']");
79
+ if (!this.IsEmail(email.val())) {
80
+ $(email).addClass('error_input');
81
+ this.displayFormError(formType, 'Improper Email Format');
82
+ this.disableForm(formType, false);
83
+ this.hideSpinner();
84
+ return xhr.abort();
85
+ }
86
+ };
87
+ this.IsEmail = function(email) {
88
+ var regex;
89
+ regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
90
+ return regex.test(email);
91
+ };
92
+ this.setTabHeader = function(formType) {
93
+ var tabHeaderID;
94
+ tabHeaderID = "" + formType + "Button";
95
+ $('#feedback_form_holder .ld_tab').removeClass('tab_active');
96
+ return this.select(tabHeaderID).addClass('tab_active');
97
+ };
98
+ this.setTabContent = function(formType) {
99
+ var tabContentID;
100
+ tabContentID = "" + formType + "FormContainer";
101
+ this.displayForm(formType);
102
+ $('#feedback_form_holder .feedback_popup_tab').removeClass('active_tab');
103
+ return this.select(tabContentID).addClass('active_tab');
104
+ };
105
+ this.displayTab = function(formType) {
106
+ this.setTabHeader(formType);
107
+ return this.setTabContent(formType);
108
+ };
109
+ this.displayThankYou = function(formType) {
110
+ this.select("" + formType + "Form").hide();
111
+ return this.select("" + formType + "FormThanks").show();
112
+ };
113
+ this.displayForm = function(formType) {
114
+ this.select("" + formType + "Form").show();
115
+ return this.select("" + formType + "FormThanks").hide();
116
+ };
117
+ this.displayFormError = function(formType, msg) {
118
+ return this.select("" + formType + "FormContainer").find('.error_msg').removeClass('hidden').text(msg);
119
+ };
120
+ this.disableForm = function(formType, value) {
121
+ if (value == null) {
122
+ value = true;
123
+ }
124
+ this.select("" + formType + "Form").find("input[type=text], textarea").attr('readonly', value);
125
+ return this.select("" + formType + "Form").find("input[type=submit]").attr('disabled', value);
126
+ };
127
+ this.clearForm = function(formType) {
128
+ this.select("" + formType + "Form").find("input[type=text], textarea").val('');
129
+ this.disableForm(formType, false);
130
+ this.clearErrors(formType);
131
+ return this.hideSpinner();
132
+ };
133
+ this.showSpinner = function() {
134
+ return this.select('spinnerClass').removeClass('hidden');
135
+ };
136
+ this.hideSpinner = function() {
137
+ return this.select('spinnerClass').addClass('hidden');
138
+ };
139
+ this.clearErrors = function(formType) {
140
+ this.select("" + formType + "Form").find("input[type=text], textarea").removeClass('error_input');
141
+ return this.select("" + formType + "FormContainer").find('.error_msg').addClass('hidden');
142
+ };
143
+ return this.after('initialize', function() {
144
+ this.on(this.attr.questionForm, 'submit', function(event) {
145
+ return this.handleFormSubmit(event, 'question');
146
+ });
147
+ this.on(this.attr.feedbackForm, 'submit', function(event) {
148
+ return this.handleFormSubmit(event, 'feedback');
149
+ });
150
+ this.on(this.attr.feedbackButton, 'click', function() {
151
+ return this.displayTab('feedback');
152
+ });
153
+ return this.on(this.attr.questionButton, 'click', function() {
154
+ return this.displayTab('question');
155
+ });
156
+ });
157
+ };
158
+ return defineComponent(FormComponent);
159
+ });
160
+
161
+ }).call(this);
@@ -0,0 +1,133 @@
1
+ define [
2
+ 'jquery',
3
+ 'flight/lib/component'
4
+ ], (
5
+ $,
6
+ defineComponent,
7
+ ) ->
8
+
9
+ FormComponent = ->
10
+ @defaultAttrs
11
+ ticketSubmitRoute: '/support/tickets/'
12
+ spinnerClass: '.spinner'
13
+
14
+ questionForm: '#questions_form_element'
15
+ questionFormThanks: '#thanks_questions_tab'
16
+ questionButton: '#questions_form'
17
+ questionFormContainer: '#questions_form_tab'
18
+ questionBrowserInput: '#question_browser'
19
+ questioncurrentURL: '#question_current_url'
20
+
21
+ feedbackForm: '#feedback_form_element'
22
+ feedbackFormThanks: '#thanks_feedback_tab'
23
+ feedbackButton: '#feedback_form'
24
+ feedbackFormContainer: '#feedback_form_tab'
25
+ feedbackBrowserInput: '#feedback_browser'
26
+ feedbackcurrentURL: '#feedback_current_url'
27
+
28
+ @handleFormSubmit = (ev, formType) ->
29
+ @disableForm(formType)
30
+ @populateMetadata(formType)
31
+ $.ajax
32
+ url: @attr.ticketSubmitRoute
33
+ type: "POST"
34
+ data: @select("#{formType}Form").serialize()
35
+ dataType: "json"
36
+ beforeSend: (xhr, opts) =>
37
+ @showSpinner()
38
+ @clearErrors(formType)
39
+ @checkFormErrors(formType, xhr)
40
+ success: (response, textStatus) =>
41
+ if response.ticket
42
+ @displayThankYou(formType)
43
+ @clearForm(formType)
44
+ @trigger('supportTicketSubmitted')
45
+ error: (jqXHR, textStatus) =>
46
+ @displayFormError(formType, 'Failed to submit ticket. Please try again later.')
47
+ @disableForm(formType, false)
48
+ @hideSpinner()
49
+
50
+ # IE8 support
51
+ if ev.preventDefault then ev.preventDefault() else ev.returnValue = false
52
+
53
+ @populateMetadata = (formType) ->
54
+ browserInfo = navigator.userAgent
55
+ currentURL = window.location.href
56
+ @select("#{formType}BrowserInput").val(browserInfo)
57
+ @select("#{formType}currentURL").val(currentURL)
58
+
59
+ @checkFormErrors = (formType, xhr) ->
60
+ blank_inputs = @select("#{formType}Form").find("input:text[value=''], textarea[value='']")
61
+ if blank_inputs.length > 0
62
+ $(blank_inputs).addClass('error_input')
63
+ @displayFormError(formType, 'Missing Required Field')
64
+ @disableForm(formType, false)
65
+ @hideSpinner()
66
+ xhr.abort()
67
+ return
68
+ email = @select("#{formType}Form").find("input[name='ticket[requester][email]']")
69
+ unless @IsEmail(email.val())
70
+ $(email).addClass('error_input')
71
+ @displayFormError(formType, 'Improper Email Format')
72
+ @disableForm(formType, false)
73
+ @hideSpinner()
74
+ xhr.abort()
75
+
76
+ @IsEmail = (email) ->
77
+ regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/
78
+ regex.test email
79
+
80
+ @setTabHeader = (formType) ->
81
+ tabHeaderID = "#{formType}Button"
82
+ $('#feedback_form_holder .ld_tab').removeClass('tab_active')
83
+ @select(tabHeaderID).addClass('tab_active')
84
+
85
+ @setTabContent = (formType) ->
86
+ tabContentID = "#{formType}FormContainer"
87
+ @displayForm(formType)
88
+ $('#feedback_form_holder .feedback_popup_tab').removeClass('active_tab')
89
+ @select(tabContentID).addClass('active_tab')
90
+
91
+ @displayTab = (formType) ->
92
+ @setTabHeader(formType)
93
+ @setTabContent(formType)
94
+
95
+ @displayThankYou = (formType) ->
96
+ @select("#{formType}Form").hide()
97
+ @select("#{formType}FormThanks").show()
98
+
99
+ @displayForm = (formType) ->
100
+ @select("#{formType}Form").show()
101
+ @select("#{formType}FormThanks").hide()
102
+
103
+ @displayFormError = (formType, msg) ->
104
+ @select("#{formType}FormContainer").find('.error_msg').removeClass('hidden').text(msg)
105
+
106
+ @disableForm = (formType, value=true) ->
107
+ @select("#{formType}Form").find("input[type=text], textarea").attr('readonly', value)
108
+ @select("#{formType}Form").find("input[type=submit]").attr('disabled', value)
109
+
110
+ @clearForm = (formType) ->
111
+ @select("#{formType}Form").find("input[type=text], textarea").val('')
112
+ @disableForm(formType, false)
113
+ @clearErrors(formType)
114
+ @hideSpinner()
115
+
116
+ @showSpinner = ->
117
+ @select('spinnerClass').removeClass('hidden')
118
+
119
+ @hideSpinner = ->
120
+ @select('spinnerClass').addClass('hidden')
121
+
122
+ @clearErrors = (formType) ->
123
+ @select("#{formType}Form").find("input[type=text], textarea").removeClass('error_input')
124
+ @select("#{formType}FormContainer").find('.error_msg').addClass('hidden')
125
+
126
+ @after 'initialize', ->
127
+ @on @attr.questionForm, 'submit', (event)-> @handleFormSubmit(event, 'question')
128
+ @on @attr.feedbackForm, 'submit', (event)-> @handleFormSubmit(event, 'feedback')
129
+ @on @attr.feedbackButton, 'click', -> @displayTab('feedback')
130
+ @on @attr.questionButton, 'click', -> @displayTab('question')
131
+
132
+
133
+ defineComponent FormComponent