Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
moving the north_harbor demo under this for documentation
- Loading branch information
Showing
105 changed files
with
9,850 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
db/schema.rb | ||
db/*.sqlite3 | ||
config/database.yml | ||
log/*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1.8.7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
I have to admit, this app, as far as real solutions go, has some problems: | ||
- There is too much logic in the views for my taste. Of course, that is what the StoneWall gem would address, and this isn't about stonewall (yet). | ||
- I don't explain some of the tradeoffs in the modeling made. I'll explain it in a class based on this, but why, for example, don't I have tasks for data entry and verification? The short story - when the workflow is linear and the work to be done is done by the owner, it is acceptable to reduce the workflow down to 'linear, done by owner', and not worry about tasks. | ||
- It needs to have tests. In the context that I change this app constantly to prove different points, the tests would slow that down. A future version based on real coursework would have tests though. | ||
|
||
|
||
|
||
|
||
|
||
This is an example app using StonePath. | ||
|
||
It isn't perfect yet, and the scope has diverged slightly since I wrote these requirements, but it is good enough for now, and it shows how to do some useful stuff with Stonepath. I'll be adding more to this very shortly. | ||
|
||
This app shows: | ||
|
||
- simple stonepath workflow | ||
- restful conventions | ||
- highjacking the ActiveRecord errors hash. | ||
|
||
---- | ||
North Harbor Coffee company is a small chain of coffee shops in the Pacific Northwest of the United States. They have approximately 20 stores, and they are opening a few new ones every quarter. | ||
|
||
Todd Saboe, the Director of Marketing for NHCC has decided they need a consolidated company-wide system for handling customer complaints. These complaints might be left in a "complaints or suggestions" box in each store, they might be mailed (or emailed) to people at the company, they might come in via a phone call or fax, or might even be the result of a customer survey. Where they come from isn't important, as long as the company has a consolidated way to handle (and report on) all complaints. | ||
|
||
Complaints can be categorized as follows: | ||
1) Complaint about service | ||
"I waited forever for my drink, the barista was rude, and my coffee was wrong" | ||
2) Complaint about quality | ||
"Your beans are burnt and the coffee is bitter" | ||
3) Complaint about details of a specific location | ||
"The parking here is horrible and the drive-through blocks the main traffic" | ||
4) Complaint about the company overall | ||
"You guys exploit the peasant farmers of Guatemala" | ||
|
||
And they are typed as follows: | ||
1) Out of Scope - We might be exploiting peasant farmers, but that isn't something this system is meant to address. We might send those up the chain to management, but this system is about managing store quality and customer experience. | ||
2) Minor complaint - the store manager or other appropriate parties should be notified about the complaint, but no followup is necessary. | ||
3) Intermediate complaint - We will assign some action that the store manager should follow up on based on this complaint. The store manager should take action and record it in the system. | ||
4) Major complaint - This category is referred to the corporate attorney and the store manager. Notes about this complaint are tracked in this system. | ||
|
||
When a complaint is about a particular store, that store's Identification number will be entered along with the complaint. This number is displayed in the store and printed out on every receipt. | ||
|
||
The complaint will have a description, generally the text of the complaint as provided by the complainer. | ||
|
||
Users of the system can attach comments to the complaint. | ||
|
||
If complaints result in any follow-up actions necessary by the store, those will be tracked along with the complaint. Actions have a description, comments, and a due date. If a store has an issue with any of these actions, there is an 'appeals' process where they can make an alternate suggestion for how to resolve the issue and kick it back to the "Customer Satisfaction Manager". | ||
|
||
|
||
The workflow associated with a complaint is straightforward, but varies by the type. | ||
|
||
1) The complaint is entered into the system. | ||
Various different people enter the complaints. For instance, all of the complaints stuffed into the 'suggestions' boxes are sent to a third party company that does data entry. We may have the help desk answering phones enter complaints, and in the future, we may have a 'complaints and suggestions' box on our corporate website. | ||
|
||
2) There are several employees with the title of "Customer Satisfaction Manager". They will receive the complaints, categorize and rate them as above. Depending on the rating, several different workflow paths will occur: | ||
|
||
|
||
Out of Scope Path: | ||
1) Out of Scope complaints stop here, and are pulled into one great big report every quarter. | ||
|
||
Minor Complaint Path: | ||
1) Minor complaints are sent to the store/store manager. The store manager can then mark them as 'addressed' or 'ignored', as they see fit. As always, they can attach any comments they want to this complaint. | ||
|
||
Intermediate Complaint Path: | ||
1) Intermediate complaints are sent to a "Customer Satisfaction Specialist" to handle as per their job. The store manager is also notified the complaint exists and can add comments to it. The Customer Satisfaction Specialist is free to do whatever is necessary to investigate the complaint, including talk to the store manager, employees, or even the customer if contact info is provided. The Customer resolution specialist can create one or more "Follow-up actions" for the store manager. | ||
|
||
2) The store manager looks at each follow-up action and is responsible for implementing the action by the due date provided. (These may come up during the store's annual inspection, but that is outside the scope of this process). The store manager can also 'kick back' the action if they disagree. The kickback can go to either the Customer Resolution Specialist that created the follow-up action, or it can go to their manager, at the discretion of the store manager. | ||
|
||
3) If kicked back, the action is either cancelled, or sent back to the manager as is, or potentially modified. | ||
|
||
|
||
Major Complaint Path: | ||
1) Major complaints are assigned to the corporate attorney and the store manager is notified. At this point it is marked "Under Investigation", and everyone is free to add comments to the complaint. | ||
|
||
2) At some point the lawyer marks this complaint as "addressed", and is free to add whatever comments are necessary. The system doesn't track any other details of this workflow. | ||
|
||
|
||
Assignment: What are the roles identified above? | ||
|
||
Roles of the system: | ||
Data Entry | ||
Store/Store Manager | ||
Customer Satisfaction Manager | ||
Customer Satisfaction Specialist | ||
Corporate Attorney | ||
|
||
What are the classes identified, and what StonePath Entities are they? | ||
|
||
Classes in the domain: | ||
User (workbench) | ||
Role (workbench) | ||
Complaint (workitem, commentable)) | ||
Comment (just a polymorphic activerecord class) | ||
Follow-up Actions (workitem, commentable, maybe just a custom task) | ||
Tasks (task) | ||
Store (workbench?) | ||
|
||
|
||
What is the workflow of the Complaint workitem? | ||
|
||
complaint workflow: | ||
|
||
In Creation -> CSM Review -> Out of Scope | ||
-> Store Manager Review -> Addressed | ||
-> Ignored | ||
-> CSS Review -> Pending Action -> Addressed | ||
-> Attorney Review -> Addressed | ||
|
||
What is the workflow of the Action Workitem? Could this simply be modeled as a custom task? | ||
Action Workflow: | ||
Defining -> Pending -> Completed | ||
-> Kicked Back -> &Pending | ||
-> Cancelled | ||
|
||
|
||
What about 'owner'? If most actions in the system are taken care of by tasking, then is the owner even necessary? | ||
|
||
Creation - owner is the data entry clerk. | ||
CSM review - can stay data entry | ||
Out of Scope - no owner | ||
Store Manager Review - Store Owner | ||
CSS Review - CSS | ||
Attorney Purvue - Attorney | ||
Addressed/Ignored - no owner | ||
|
||
|
||
or, maybe it always stays the data entry clerk | ||
|
||
no right answer - depends o how you want to use it in the domain |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Add your own tasks in files placed in lib/tasks ending in .rake, | ||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. | ||
|
||
require(File.join(File.dirname(__FILE__), 'config', 'boot')) | ||
|
||
require 'rake' | ||
require 'rake/testtask' | ||
require 'rdoc/task' | ||
|
||
require 'tasks/rails' | ||
|
||
|
||
#require(File.join(File.dirname(__FILE__), 'vendor', 'gems', "**", 'lib', 'tasks', '*.rake')) | ||
|
||
Dir["#{RAILS_ROOT}/vendor/gems/*/lib/tasks/*.rake"].sort.each { |rakefile| load rakefile } | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Filters added to this controller apply to all controllers in the application. | ||
# Likewise, all the methods added will be available for all controllers. | ||
|
||
class ApplicationController < ActionController::Base | ||
include Authentication | ||
include SentientController | ||
helper :all # include all helpers, all the time | ||
protect_from_forgery # See ActionController::RequestForgeryProtection for details | ||
|
||
# Scrub sensitive parameters from your log | ||
# filter_parameter_logging :password | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
class ComplaintsController < ApplicationController | ||
before_filter :login_required | ||
|
||
# GET /complaints | ||
# GET /complaints.xml | ||
def index | ||
@complaints = Complaint.all | ||
|
||
respond_to do |format| | ||
format.html # index.html.erb | ||
format.xml { render :xml => @complaints } | ||
end | ||
end | ||
|
||
# GET /complaints/1 | ||
# GET /complaints/1.xml | ||
def show | ||
@complaint = Complaint.find(params[:id]) | ||
|
||
respond_to do |format| | ||
format.html # show.html.erb | ||
format.xml { render :xml => @complaint } | ||
end | ||
end | ||
|
||
# GET /complaints/new | ||
# GET /complaints/new.xml | ||
def new | ||
@complaint = Complaint.new | ||
@managers = User.find_all_by_role("customer_satisfaction_manager") | ||
respond_to do |format| | ||
format.html # new.html.erb | ||
format.xml { render :xml => @complaint } | ||
end | ||
end | ||
|
||
# GET /complaints/1/edit | ||
def edit | ||
@complaint = Complaint.find(params[:id]) | ||
@managers = User.find_all_by_role("customer_satisfaction_manager") | ||
end | ||
|
||
# POST /complaints | ||
# POST /complaints.xml | ||
def create | ||
@complaint = Complaint.new(params[:complaint]) | ||
|
||
respond_to do |format| | ||
if @complaint.save | ||
flash[:notice] = 'Complaint was successfully created.' | ||
format.html { redirect_to(@complaint) } | ||
format.xml { render :xml => @complaint, :status => :created, :location => @complaint } | ||
else | ||
format.html { render :action => "new" } | ||
format.xml { render :xml => @complaint.errors, :status => :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
# PUT /complaints/1 | ||
# PUT /complaints/1.xml | ||
def update | ||
@complaint = Complaint.find(params[:id]) | ||
|
||
respond_to do |format| | ||
if @complaint.update_attributes(params[:complaint]) | ||
flash[:notice] = 'Complaint was successfully updated.' | ||
format.html { redirect_to(@complaint) } | ||
format.xml { head :ok } | ||
else | ||
format.html { render :action => "edit" } | ||
format.xml { render :xml => @complaint.errors, :status => :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
# DELETE /complaints/1 | ||
# DELETE /complaints/1.xml | ||
def destroy | ||
@complaint = Complaint.find(params[:id]) | ||
@complaint.destroy | ||
|
||
respond_to do |format| | ||
format.html { redirect_to(complaints_url) } | ||
format.xml { head :ok } | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class DashboardController < ApplicationController | ||
before_filter :login_required | ||
|
||
def index | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Any workitem or task implements a state machine, and will have events that | ||
# can be triggered from the current state. This controller does that. | ||
# as long as the routes file has events as a nested resource of the workitem's controller, | ||
# you can say: | ||
# [post] /workitem/:id/event | ||
class EventsController < ApplicationController | ||
|
||
before_filter :setup_parentage | ||
|
||
def index | ||
@events = @parentage.last.aasm_events_for_current_state | ||
end | ||
|
||
def new | ||
@event = params[:name] | ||
@workitem = @parentage.last | ||
|
||
if @workitem.class.aasm_events[@event.to_sym].respond_to?(:required_data) | ||
@required_data = @workitem.class.aasm_events[@event.to_sym].required_data | ||
end | ||
end | ||
|
||
def create | ||
if @parentage.last.send(params[:name] + "!", nil, params) | ||
# set flash, redirect to workitem. Other logic here when we get smart. | ||
flash[:notice] = "Entered #{@parentage.last.aasm_state} state." | ||
else | ||
flash[:error] = @parentage.last.errors.full_messages | ||
end | ||
redirect_to polymorphic_path(@parentage) | ||
end | ||
|
||
private | ||
|
||
# need to figure out how to simulate a url in testing. mocking maybe? | ||
def setup_parentage | ||
parent_path_parts = request.path.split("/") | ||
parent_path_parts.shift | ||
@parentage = Array.new | ||
parent_path_parts.each_slice(2) do |k,v| | ||
@parentage << k.classify.constantize.find(v.to_i) unless (k == "events" || v.nil?) | ||
end | ||
end | ||
end |
81 changes: 81 additions & 0 deletions
81
north_harbor/app/controllers/follow_up_actions_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
class FollowUpActionsController < ApplicationController | ||
before_filter :login_required, :setup_complaint | ||
|
||
def index | ||
@follow_up_actions = Complaint.find(params[:complaint_id]).follow_up_actions | ||
|
||
respond_to do |format| | ||
format.html # index.html.erb | ||
format.xml { render :xml => @follow_up_actionss } | ||
end | ||
end | ||
|
||
def show | ||
@follow_up_action = Complaint.find(params[:complaint_id]).follow_up_actions.find(params[:id]) | ||
|
||
respond_to do |format| | ||
format.html # show.html.erb | ||
format.xml { render :xml => @follow_up_actions } | ||
end | ||
end | ||
|
||
def new | ||
@follow_up_action = FollowUpAction.new | ||
workbench = @complaint.store.manager | ||
@follow_up_action.workbench = workbench | ||
respond_to do |format| | ||
format.html # new.html.erb | ||
format.xml { render :xml => @follow_up_actions } | ||
end | ||
end | ||
|
||
def edit | ||
@follow_up_action = Complaint.find(params[:complaint_id]).follow_up_actions.find(params[:id]) | ||
|
||
end | ||
|
||
def create | ||
@follow_up_action = @complaint.follow_up_actions.new(params[:follow_up_action]) | ||
@follow_up_action.workbench = User.find params[:follow_up_action][:workbench_id] | ||
respond_to do |format| | ||
if @follow_up_action.save | ||
flash[:notice] = 'FollowUpAction was successfully created.' | ||
format.html { redirect_to([@complaint, @follow_up_actions]) } | ||
format.xml { render :xml => @follow_up_actions, :status => :created, :location => @follow_up_actions } | ||
else | ||
format.html { render :action => "new" } | ||
format.xml { render :xml => @follow_up_actions.errors, :status => :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
def update | ||
@follow_up_action = Complaint.find(params[:complaint_id]).follow_up_actions.find(params[:id]) | ||
@follow_up_action.workbench = User.find params[:follow_up_action][:workbench_id] | ||
|
||
respond_to do |format| | ||
if @follow_up_action.update_attributes(params[:follow_up_action]) | ||
flash[:notice] = 'FollowUpAction was successfully updated.' | ||
format.html { redirect_to(@follow_up_actions) } | ||
format.xml { head :ok } | ||
else | ||
format.html { render :action => "edit" } | ||
format.xml { render :xml => @follow_up_action.errors, :status => :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
def destroy | ||
@follow_up_action = Complaint.find(params[:complaint_id]).follow_up_actions.find(params[:id]) | ||
@follow_up_action.destroy | ||
|
||
respond_to do |format| | ||
format.html { redirect_to(complaint_follow_up_actions_url(@complaint)) } | ||
format.xml { head :ok } | ||
end | ||
end | ||
|
||
def setup_complaint | ||
@complaint = Complaint.find(params[:complaint_id]) | ||
end | ||
end |
Oops, something went wrong.