From a8968f20cd3b06b2cc19de6871019d4aaae6b79b Mon Sep 17 00:00:00 2001 From: Jason Woofenden Date: Thu, 13 Nov 2008 07:41:29 -0500 Subject: [PATCH] ready to roll. still needs e-mails and prioritizing --- code/config.php | 4 ++ index.html | 33 +++++---- index.php | 34 +++++---- tasks.css | 2 +- tasks.html | 31 ++++++++- tasks.php | 192 +++++++++++++++++++++++++++++++++++++++++++++------ tiny_agreement.html | 23 ++++++ tiny_agreement.php | 47 +++++++++++++ 8 files changed, 314 insertions(+), 52 deletions(-) create mode 100644 tiny_agreement.html create mode 100644 tiny_agreement.php diff --git a/code/config.php b/code/config.php index 60d5c1f..1e59d64 100644 --- a/code/config.php +++ b/code/config.php @@ -20,3 +20,7 @@ function logged_in_as_admin() { function logged_in_as_contractor() { return logged_in_as_admin(); } + +function enc_money($float) { + return format_money($float, $cents = true); +} diff --git a/index.html b/index.html index fd63432..d623af8 100644 --- a/index.html +++ b/index.html @@ -11,24 +11,19 @@

Progress Manager

-

This page is for commissioning Jason Woofenden, working out the details of the tasks, costs and priorities.

+

This page is for giving Jason Woofenden work and figuring out the details of the associated tasks, costs and priorities.

-

Commission a new feature/updateReport a problem

+

Add a taskReport a problem

- +

Tasks needing your attention:

- -
Task #~task_id~: ~task_title.html~ (~task_state~)
- - - -

Prioritized queue

-

Use the arrows on the left to change the order.

- -
Task #~task_id~: ~task_title.html~ (~task_price.money~)
+ +
Task #~task_id~: ~task_title.html~ (~task_state~)
+ + +
Task #~task_id~: ~task_title.html~ (~task_state~)
-

Tasks waiting for Jason:

@@ -38,14 +33,22 @@ -

Jason is currently working on:

+

Jason is currently working on:

Task #~task_id~: ~task_title.html~ (~task_price.money~)
+ +

Queued tasks

+
Jason hasn't started on these yet, so you can change them if you want. At some point you'll also be able to prioritize them.
+ +
Task #~task_id~: ~task_title.html~ (~task_price.money~)
+ + + -

Finished tasks (unpaid)

+

Finished tasks

Task #~task_id~: ~task_title.html~ (~task_price.money~)
diff --git a/index.php b/index.php index d87d74f..46a27e3 100644 --- a/index.php +++ b/index.php @@ -2,10 +2,6 @@ require_once('code/tasks.php'); -function enc_money($float) { - return format_money($float, $cents = true); -} - function index_main() { if(!logged_in()) { return 'login'; @@ -22,7 +18,6 @@ function task_summary($tem_prefix, $where_clause/*, ... */) { $args = func_get_args(); $args = array_slice($args, 1); array_unshift($args, 'tasks', 'id,price,title,state,client_id'); - print_r($args); $rows = call_user_func_array('db_get_rows', $args); #$rows = db_get_rows('tasks', 'id,price,title,state,client_id', $where_clause); if($rows) { @@ -48,18 +43,31 @@ function task_summary($tem_prefix, $where_clause/*, ... */) { function _index_main() { $client_id = logged_in(); + + # make sure they've filled out the tiny user agreement + $tiny_agreement = db_get_value('people', 'tiny_agreement', 'where id=%i', $client_id); + if($tiny_agreement < 30) { + return './tiny_agreement'; + } + if(logged_in_as_contractor()) { - task_summary('needs_attention', 'where state=%i || state=%i || state=%i order by id desc', TASK_NEEDS_QUOTE, TASK_WORKING, TASK_BUG); + tem_show('needs_attention_header'); + task_summary('needs_approval', 'where state=%i order by id desc', TASK_WORKING); + task_summary('needs_fixing', 'where state=%i || state=%i order by id desc', TASK_NEEDS_QUOTE, TASK_BUG); task_summary('finished_unpaid', 'where state=%i && paid = 0 order by id desc', TASK_FINISHED); task_summary('finished_paid', 'where state=%i && paid = 1 order by id desc', TASK_FINISHED); + task_summary('queue', 'where state=%i order by client_id, ord', TASK_QUEUED); } else { - task_summary('needs_attention', 'where state=' . TASK_DRAFT . ' || state=' . TASK_NEEDS_CLARIFICATION . ' || state=' . TASK_NEEDS_GO_AHEAD . ' || state=' . TASK_NEEDS_TESTING . ' order by id'); - task_summary('queue', 'where state=' . TASK_QUEUED . " && client_id=$client_id order by ord"); - task_summary('jason', 'where state=' . TASK_NEEDS_QUOTE . ' || state=' . TASK_WORKING . ' || state=' . TASK_BUG . ' order by id desc'); - #task_summary('jason_pricing', 'where state=' . TASK_NEEDS_QUOTE . ' order by id desc'); - #task_summary('jason_working', 'where state=' . TASK_WORKING . ' order by id desc'); - task_summary('finished_unpaid', 'where state=' . TASK_FINISHED . ' && paid = 0 order by id desc'); - task_summary('finished_paid', 'where state=' . TASK_FINISHED . ' && paid = 1 order by id desc'); + if(db_count('tasks', 'where client_id=%i && (state=%i || state=%i || state=%i || state=%i)', $client_id, TASK_DRAFT, TASK_NEEDS_CLARIFICATION, TASK_NEEDS_GO_AHEAD, TASK_NEEDS_TESTING)) { + tem_show('needs_attention_header'); + task_summary('needs_approval', 'where client_id=%i && (state=%i || state=%i) order by id', $client_id, TASK_NEEDS_GO_AHEAD, TASK_NEEDS_TESTING); + task_summary('needs_fixing', 'where client_id=%i && (state=%i || state=%i) order by id', $client_id, TASK_DRAFT, TASK_NEEDS_CLARIFICATION); + } + task_summary('queue', 'where client_id=%i && state=%i order by ord', $client_id, TASK_QUEUED); + task_summary('jason', 'where client_id=%i && (state=%i || state=%i) order by id desc', $client_id, TASK_NEEDS_QUOTE, TASK_BUG); + task_summary('jason_working', 'where client_id=%i && state=%i order by id desc', $client_id, TASK_WORKING); + task_summary('finished_unpaid', 'where client_id=%i && state=%i && paid=0 order by id desc', $client_id, TASK_FINISHED); + task_summary('finished_paid', 'where client_id=%i && state=%i && paid=1 order by id desc', $client_id, TASK_FINISHED); } return; } diff --git a/tasks.css b/tasks.css index e7a6c7c..49cb284 100644 --- a/tasks.css +++ b/tasks.css @@ -1,4 +1,4 @@ -.index_body h3 { +.index_body h3, .index_body h4 { text-align: left; margin: 15px 0px 5px; } diff --git a/tasks.html b/tasks.html index efc99c4..448e989 100644 --- a/tasks.html +++ b/tasks.html @@ -12,6 +12,7 @@

Task #~task_id~Problem Report "~title.html~"

+

Title: ~title.html~

Status: ~state.html~

@@ -21,6 +22,19 @@

Narrative:
~description.htmlbrtab~

+

Price: ~price.money~

+ +

Approve price

+ +

Edit this task

+

Edit this task

+ +

I'm starting on this now.

+ +

Mark this task as finished (request testing).

+ +

Please test everything in the narrative! If everything is working, Mark this task as finished Otherwise please e-mail or call Jason.

+

Back

@@ -35,10 +49,23 @@ URL: - Below, write as if the change/addition you would like has already been implemented. Write a first-person narrative with the specifics of what you do and what you see as a result. See an example.Below, describe in detail 1) what you do, 2) what you expect to see, 3) what you see instead + + The narrative below contains "FIXME". This is Jason's way of marking the places in the narrative that need your attention. Please update these parts of the narrative to be more complete/specific and remove the "FIXME". + Mark trouble spots with "FIXME". + Below, write as if the change/addition you would like has already been implemented. Write a first-person narrative with the specifics of what you do and what you see as a result. See an example. + Below, describe in detail 1) what you do, 2) what you expect to see, 3) what you see instead + + + Price: + + + + + + - + diff --git a/tasks.php b/tasks.php index 6351ca6..03bca46 100644 --- a/tasks.php +++ b/tasks.php @@ -4,7 +4,6 @@ require_once('code/tasks.php'); $GLOBALS['tasks_form_recipient'] = "fixme@example.com"; -define('TASKS_DB_FIELDS', 'title,url,description,state'); require_once('code/wfpl/template.php'); @@ -13,6 +12,10 @@ require_once('code/wfpl/messages.php'); require_once('code/wfpl/email.php'); require_once('code/db_connect.php'); +function description_has_fixmes($description) { + return (strpos($description, 'FIXME') !== false); +} + # replace every character in $str with " " function tonbsp($matches) { return str_repeat(' ', strlen($matches[0]) * 2); @@ -29,16 +32,18 @@ function tasks_get_fields() { $title = format_oneline($_REQUEST['title']); $url = format_oneline($_REQUEST['url']); $description = format_unix($_REQUEST['description']); + $price = format_decimal($_REQUEST['price']); - tasks_tem_sets($title, $url, $description); + tasks_tem_sets($title, $url, $description, $price); - return array($title, $url, $description); + return array($title, $url, $description, $price); } -function tasks_tem_sets($title, $url, $description) { +function tasks_tem_sets($title, $url, $description, $price) { tem_set('title', $title); tem_set('url', $url); tem_set('description', $description); + tem_set('price', $price); } function tasks_main() { @@ -66,19 +71,77 @@ function tasks_main() { function tasks_display_main() { $task_id = format_int($_REQUEST['tasks_id']);; - $row = db_get_row('tasks', 'title,url,description,state', 'where id=%i', $task_id); + $client_id = logged_in(); + if(logged_in_as_contractor()) { + $row = db_get_row('tasks', 'title,url,description,state,price', 'where id=%i', $task_id); + } else { + $row = db_get_row('tasks', 'title,url,description,state,price', 'where id=%i && client_id=%i', $task_id, $client_id); + } if($row) { - list($title, $url, $description, $state) = $row; + list($title, $url, $description, $state, $price) = $row; tem_set('task_id', $task_id); tem_set('title', $title); tem_set('url', $url); tem_set('description', $description); tem_set('state', task_state_pretty($state)); + tem_set('price', $price); if($state == TASK_BUG) { tem_show('bug_title'); } else { tem_show('normal_title'); } + if(logged_in_as_contractor()) { + switch($state) { + case TASK_DRAFT: + case TASK_NEEDS_CLARIFICATION: + case TASK_NEEDS_QUOTE: + case TASK_BUG: + case TASK_NEEDS_GO_AHEAD: + tem_show('normal_edit_link'); + tem_show('price_row'); + break; + case TASK_QUEUED: + tem_show('normal_edit_link'); + tem_show('working_link'); + tem_show('price_row'); + case TASK_WORKING: + tem_show('price_row'); + tem_show('needs_testing_link'); + break; + case TASK_NEEDS_TESTING: + case TASK_FINISHED: + tem_show('price_row'); + break; + } + } else { + switch($state) { + case TASK_DRAFT: + case TASK_NEEDS_CLARIFICATION: + case TASK_NEEDS_QUOTE: + case TASK_BUG: + tem_show('normal_edit_link'); + break; + case TASK_NEEDS_GO_AHEAD: + tem_show('price_row'); + tem_show('approve_price_link'); + tem_show('warning_edit_link'); + break; + case TASK_QUEUED: + tem_show('price_row'); + tem_show('warning_edit_link'); + break; + case TASK_WORKING: + tem_show('price_row'); + break; + case TASK_NEEDS_TESTING: + tem_show('price_row'); + tem_show('finished_link'); + break; + case TASK_FINISHED: + tem_show('price_row'); + break; + } + } } else { message("Task #$task_id not found"); return './'; @@ -87,9 +150,18 @@ function tasks_display_main() { function tasks_edit_main() { $state = TASK_DRAFT; # will be overwritten + $client_id = logged_in(); # fixed shortly if we're contractor $edit_id = format_int($_REQUEST['tasks_edit_id']); unset($_REQUEST['tasks_edit_id']); if($edit_id) { + $owner = db_get_value('tasks', 'client_id', 'where id=%i', $edit_id); + if(logged_in_as_contractor()) { + $client_id = $owner; + } elseif($owner != $client_id) { + message('Sorry, that task was entered by/for another client.'); + return './'; + } + # add hidden field for database id of row we're editing tem_set('tasks_edit_id', $edit_id); tem_show('editing'); @@ -98,16 +170,54 @@ function tasks_edit_main() { } if(isset($_REQUEST['tasks_new_bug'])) { - tem_show('bug_submit'); $state = TASK_BUG; - } else { - tem_show('normal_submits'); } - if($state == TASK_BUG) { - tem_show('bug_instructions'); - } else { - tem_show('normal_instructions'); + if(isset($_REQUEST['tasks_approve_price_id'])) { + $id = $_REQUEST['tasks_approve_price_id']; + $owner = db_get_value('tasks', 'client_id', 'where id=%i', $id);; + if(logged_in() != $owner) { + message("Error: can't approve a task entered by/for another client."); + return './'; + } + db_update('tasks', 'state', TASK_QUEUED, 'where id=%i', $id); + message('Price approved.'); + return './'; + } + + if(isset($_REQUEST['tasks_working_id'])) { + $id = $_REQUEST['tasks_working_id']; + if(!logged_in_as_contractor()) { + message("Error: only Jason can say what he's working on."); + return './'; + } + db_update('tasks', 'state', TASK_WORKING, 'where id=%i', $id); + message('OK, client locked out of modifying that one.'); + return './'; + } + + if(isset($_REQUEST['tasks_needs_testing_id'])) { + $id = $_REQUEST['tasks_needs_testing_id']; + if(!logged_in_as_contractor()) { + message("Error: only Jason can say when he's done."); + return './'; + } + db_update('tasks', 'state', TASK_NEEDS_TESTING, 'where id=%i', $id); + message('Task awaits testing.'); + return './'; + } + + if(isset($_REQUEST['tasks_finished_id'])) { + $id = $_REQUEST['tasks_finished_id']; + $owner = db_get_value('tasks', 'client_id', 'where id=%i', $id);; + if(logged_in() != $owner) { + message("Error: can't test a task entered by/for another client."); + return './'; + } + db_update('tasks', 'state', TASK_FINISHED, 'where id=%i', $id); + message('Task marked as finished.'); + # FIXME also mark it as paid if client's balance can cover it + return './'; } $delete_id = format_int($_REQUEST['tasks_delete_id']); @@ -120,25 +230,43 @@ function tasks_edit_main() { } if(isset($_REQUEST['title'])) { - list($title, $url, $description) = tasks_get_fields(); + list($title, $url, $description, $price) = tasks_get_fields(); # FIXME if(isset($_REQUEST['save_draft'])) { $state = TASK_DRAFT; } elseif(isset($_REQUEST['save_bug'])) { $state = TASK_BUG; - } else { - $state = TASK_NEEDS_QUOTE; + } elseif(isset($_REQUEST['save_price']) && logged_in_as_contractor()) { + $tiny_agreement = db_get_value('people', 'tiny_agreement', 'where id=%i', $client_id); + if($price < $tiny_agreement) { + $state = TASK_QUEUED; + } else { + $state = TASK_NEEDS_GO_AHEAD; + } + } elseif(isset($_REQUEST['needs_clarification'])) { + $state = TASK_NEEDS_CLARIFICATION; + } else { # better be "request_price" + if(description_has_fixmes($description)) { + $state = TASK_NEEDS_CLARIFICATION; + message('The description is not ready to be priced yet because it still contains at least one "FIXME".'); + } else { + $state = TASK_NEEDS_QUOTE; + } } if("you're happy with the POSTed values") { if($edit_id) { - db_update('tasks', 'title,url,description,state', $title, $url, $description, $state, 'where id=%i', $edit_id); + if(isset($_REQUEST['price']) && logged_in_as_contractor()) { + db_update('tasks', 'title,url,description,state,price', $title, $url, $description, $state, $price, 'where id=%i', $edit_id); + } else { + db_update('tasks', 'title,url,description,state', $title, $url, $description, $state, 'where id=%i', $edit_id); + } message('Task updated.'); } else { # new task $paid = 0; - $client_id = 4; # FIXME + $client_id = logged_in(); db_insert('tasks', 'client_id,title,url,description,state,paid', $client_id, $title, $url, $description, $state, $paid); message('Task saved.'); } @@ -171,14 +299,14 @@ function tasks_edit_main() { # fix their entry in whatever way you require. } elseif($edit_id) { # we've recieved an edit id, but no data. So we grab the values to be edited from the database - list($client_id, $ord, $title, $url, $description, $state, $paid) = db_get_row('tasks', TASKS_DB_FIELDS, 'where id=%i', $edit_id); - tasks_tem_sets($client_id, $ord, $title, $url, $description, $state, $paid); + list($title, $url, $description, $state, $paid) = db_get_row('tasks', 'title,url,description,state,price', 'where id=%i', $edit_id); + tasks_tem_sets($title, $url, $description, $price); } else { # form not submitted, you can set default values like so: #tem_set('client_id', 'Yes'); } - # this has to be later in the file because it requres that client_id be set already + # display header if($edit_id) { tem_show('edit_msg'); } elseif($state == TASK_BUG) { @@ -186,6 +314,28 @@ function tasks_edit_main() { } else { tem_show('new_msg'); } + + # display instructions + if($state == TASK_BUG) { + tem_show('bug_instructions'); + if(logged_in_as_contractor()) { + tem_show('price_field'); + tem_show('contractor_submits'); + } else { + tem_show('bug_submit'); + } + } elseif($state == TASK_NEEDS_QUOTE && logged_in_as_contractor()) { + tem_show('set_price_instructions'); + tem_show('price_field'); + tem_show('contractor_submits'); + } else { + if(description_has_fixmes($description)) { + tem_show('fixme_instructions'); + } else { + tem_show('normal_instructions'); + } + tem_show('normal_submits'); + } } ?> diff --git a/tiny_agreement.html b/tiny_agreement.html new file mode 100644 index 0000000..f3dcd16 --- /dev/null +++ b/tiny_agreement.html @@ -0,0 +1,23 @@ + + + + + JasonWoof -- Process + + + + +

Tiny Job Agreement

+ +

For tiny jobs ($30 or less) it is a waste of time to wait for pricing approval. So I have my clients make the following agreement:

+ +
+

For work that I request of Jason Woofenden (whether through this site, or other forms of communication like e-mail or phone), I hereby agree to pay the fee he chooses, if that fee is less than or equal to

+
+ +

Cutting out the overhead of quotes for tiny jobs should make it so I can keep my minimum fee at $10.

+ + + diff --git a/tiny_agreement.php b/tiny_agreement.php new file mode 100644 index 0000000..eb01cf1 --- /dev/null +++ b/tiny_agreement.php @@ -0,0 +1,47 @@ + -- 1.7.10.4