support_center 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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