From 442b26e2b4f442a41fbe5fa256b12c0e01219190 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Fri, 8 Mar 2013 15:14:30 +0100 Subject: [PATCH] Pflichtenheftpunkte bearbeiten --- SL/Controller/RequirementSpecItem.pm | 120 ++++++++++++------ SL/DB/RequirementSpecItem.pm | 9 ++ SL/Presenter/RequirementSpecItem.pm | 10 +- css/requirement_spec.css | 9 ++ js/requirement_spec.js | 29 ++++- locale/de/all | 6 +- templates/webpages/requirement_spec/show.html | 98 +++++++------- .../_function_block.html | 2 +- .../_function_block_content_bottom.html | 8 +- .../_function_block_form.html | 40 ++++++ .../requirement_spec_item/_section.html | 2 +- 11 files changed, 228 insertions(+), 105 deletions(-) create mode 100644 templates/webpages/requirement_spec_item/_function_block_form.html diff --git a/SL/Controller/RequirementSpecItem.pm b/SL/Controller/RequirementSpecItem.pm index c4f6f6d09..75dc503e5 100644 --- a/SL/Controller/RequirementSpecItem.pm +++ b/SL/Controller/RequirementSpecItem.pm @@ -7,18 +7,21 @@ use parent qw(SL::Controller::Base); use Time::HiRes (); use SL::DB::RequirementSpec; +use SL::DB::RequirementSpecComplexity; use SL::DB::RequirementSpecItem; +use SL::DB::RequirementSpecRisk; use SL::Helper::Flash; use SL::JSON; use SL::Locale::String; use Rose::Object::MakeMethods::Generic ( - scalar => [ qw(requirement_spec item visible_item visible_section) ], + scalar => [ qw(requirement_spec item visible_item visible_section) ], + 'scalar --get_set_init' => [ qw(complexities risks) ], ); # __PACKAGE__->run_before('load_requirement_spec'); -__PACKAGE__->run_before('load_requirement_spec_item', only => [qw(dragged_and_dropped edit_section update_section)]); +__PACKAGE__->run_before('load_requirement_spec_item', only => [qw(dragged_and_dropped ajax_update ajax_edit)]); # # actions @@ -47,34 +50,9 @@ sub action_ajax_list { $self->render($js); } -sub action_new { - my ($self) = @_; - - eval { - my $type = ($::form->{item_type} || '') =~ m/^ (?: section | (?: sub-)? function-block ) $/x ? $::form->{item_type} : die "Invalid item_type"; - $self->{item} = SL::DB::RequirementSpecItem->new(requirement_spec_id => $::form->{requirement_spec_id}); - my $section_form = $self->presenter->render("requirement_spec_item/_${type}_form", id => create_random_id(), title => t8('Create a new section')); - - $self->render(\to_json({ status => 'ok', html => $section_form }), { type => 'json' }); - 1; - } or do { - $self->render(\to_json({ status => 'failed', error => "Exception:\n" . format_exception() }), { type => 'json' }); - } -} - -sub action_create { - my ($self) = @_; - - my $type = ($::form->{item_type} || '') =~ m/^ (?: section | (?: sub-)? function-block ) $/x ? $::form->{item_type} : die "Invalid item_type"; - - $self->render(\to_json({ status => 'failed', error => 'not good, not good' }), { type => 'json' }); -} - sub action_dragged_and_dropped { my ($self) = @_; - $::lxdebug->dump(0, "form", $::form); - my $dropped_item = SL::DB::RequirementSpecItem->new(id => $::form->{dropped_id})->load || die "No such dropped item"; my $position = $::form->{position} =~ m/^ (?: before | after | last ) $/x ? $::form->{position} : die "Unknown 'position' parameter"; @@ -87,22 +65,78 @@ sub action_dragged_and_dropped { $self->render(\'', { type => 'json' }); } -sub action_edit_section { +sub action_ajax_edit { my ($self, %params) = @_; - $self->render('requirement_spec_item/_section_form', { layout => 0 }); + + $::lxdebug->dump(0, "form", $::form); + + $self->init_visible_section($::form->{current_content_id}, $::form->{current_content_type}); + $self->item(SL::DB::RequirementSpecItem->new(id => $::form->{id})->load); + + my $js = SL::ClientJS->new; + + die "TODO: edit section" if $self->item->get_type =~ m/section/; + + if (!$self->visible_section || ($self->visible_section->id != $self->item->get_section->id)) { + my $html = $self->render('requirement_spec_item/_section', { output => 0 }, requirement_spec_item => $self->item); + $js->html('#column-content', $html); + } + + if ($self->item->get_type =~ m/function-block/) { + my $create_item = sub { + [ $_[0]->id, $self->presenter->truncate(join(' ', grep { $_ } ($_[1], $_[0]->fb_number, $_[0]->description))) ] + }; + my @dependencies = + map { [ $_->fb_number . ' ' . $_->title, + [ map { ( $create_item->($_), + map { $create_item->($_, '->') } @{ $_->sorted_children }) + } @{ $_->sorted_children } ] ] + } @{ $self->item->requirement_spec->sections }; + + my @selected_dependencies = map { $_->id } @{ $self->item->dependencies }; + + my $html = $self->render('requirement_spec_item/_function_block_form', { output => 0 }, DEPENDENCIES => \@dependencies, SELECTED_DEPENDENCIES => \@selected_dependencies); + my $id_base = $self->item->get_type . '-' . $self->item->id; + my $content_top_id = '#' . $self->item->get_type . '-content-top-' . $self->item->id; + + $js->hide($content_top_id) + ->remove("#edit_${id_base}_form") + ->insertAfter($html, $content_top_id) + ->jstree->select_node('#tree', '#fb-' . $self->item->id) + ->focus("#edit_${id_base}_description") + ->val('#current_content_type', $self->item->get_type) + ->val('#current_content_id', $self->item->id) + ->render($self); + } } -sub action_update_section { +sub action_ajax_update { my ($self, %params) = @_; - $self->item->update_attributes(title => $::form->{title}, description => $::form->{description}); + my $js = SL::ClientJS->new; + my $prefix = $::form->{form_prefix} || 'text_block'; + my $attributes = $::form->{$prefix} || {}; + + foreach (qw(requirement_spec_id parent_id position)) { + delete $attributes->{$_} if !defined $attributes->{$_}; + } + + my @errors = $self->item->assign_attributes(%{ $attributes })->validate; + return $js->error(@errors)->render($self) if @errors; + + $self->item->save; - my $result = { - id => $self->item->id, - header_html => $self->render('requirement_spec_item/_section_header', { layout => 0, output => 0 }, requirement_spec_item => $self->item), - node_name => join(' ', map { $_ || '' } ($self->item->fb_number, $self->item->title)), - }; - $self->render(\to_json($result), { type => 'json' }); + my $id_prefix = $self->item->get_type eq 'function-block' ? '' : 'sub-'; + my $html_top = $self->render('requirement_spec_item/_function_block_content_top', { output => 0 }, requirement_spec_item => $self->item, id_prefix => $id_prefix); + my $html_bottom = $self->render('requirement_spec_item/_function_block_content_bottom', { output => 0 }, requirement_spec_item => $self->item, id_prefix => $id_prefix); + $id_prefix .= 'function-block-content-'; + + SL::ClientJS->new + ->remove('#' . $prefix . '_form') + ->replaceWith('#' . $id_prefix . 'top-' . $self->item->id, $html_top) + ->replaceWith('#' . $id_prefix . 'bottom-' . $self->item->id, $html_bottom) + ->jstree->rename_node('#tree', '#fb-' . $self->item->id, $::request->presenter->requirement_spec_item_tree_node_title($self->item)) + ->render($self); } # @@ -141,4 +175,16 @@ sub init_visible_section { return $self->visible_section($self->visible_item->get_section); } +sub init_complexities { + my ($self) = @_; + + return SL::DB::Manager::RequirementSpecComplexity->get_all_sorted; +} + +sub init_risks { + my ($self) = @_; + + return SL::DB::Manager::RequirementSpecRisk->get_all_sorted; +} + 1; diff --git a/SL/DB/RequirementSpecItem.pm b/SL/DB/RequirementSpecItem.pm index f7444daba..1bc44c28d 100644 --- a/SL/DB/RequirementSpecItem.pm +++ b/SL/DB/RequirementSpecItem.pm @@ -43,6 +43,15 @@ sub _before_delete_delete_children { 1; } +sub validate { + my ($self) = @_; + + my @errors; + push @errors, t8('The title is missing.') if !$self->parent_id && !$self->title; + + return @errors; +} + sub sorted_children { my ($self) = @_; diff --git a/SL/Presenter/RequirementSpecItem.pm b/SL/Presenter/RequirementSpecItem.pm index ed4f56762..b7f377e2c 100644 --- a/SL/Presenter/RequirementSpecItem.pm +++ b/SL/Presenter/RequirementSpecItem.pm @@ -5,10 +5,16 @@ use strict; use parent qw(Exporter); use Exporter qw(import); -our @EXPORT = qw(requirement_spec_item_jstree_data requirement_spec_item_dependency_list); +our @EXPORT = qw(requirement_spec_item_tree_node_title requirement_spec_item_jstree_data requirement_spec_item_dependency_list); use Carp; +sub requirement_spec_item_tree_node_title { + my ($self, $item) = @_; + + return join(' ', map { $_ || '' } ($item->fb_number, $self->truncate($item->parent_id ? $item->description : $item->title, at => 30), '<' . $item->id . '>')); +} + sub requirement_spec_item_jstree_data { my ($self, $item, %params) = @_; @@ -16,7 +22,7 @@ sub requirement_spec_item_jstree_data { my $type = !$item->parent_id ? 'section' : 'function-block'; return { - data => join(' ', map { $_ || '' } ($item->fb_number, $item->title, '<' . $item->id . '>')), + data => $self->requirement_spec_item_tree_node_title($item), metadata => { id => $item->id, type => $type }, attr => { id => "fb-" . $item->id, href => $params{href} || '#', class => $type . '-context-menu' }, children => \@children, diff --git a/css/requirement_spec.css b/css/requirement_spec.css index 90bce82b6..761b191aa 100644 --- a/css/requirement_spec.css +++ b/css/requirement_spec.css @@ -56,3 +56,12 @@ table.rs_input_field input, table.rs_input_field select { color: #fff; background: #ccc; } + +.function-block-form { + background: rgb(235, 235, 235); + border: 1px solid rgb(0, 100, 0); +} + +.function-block-form > div { + padding: 5px; +} diff --git a/js/requirement_spec.js b/js/requirement_spec.js index 6e9aaf44a..ca2c694bb 100644 --- a/js/requirement_spec.js +++ b/js/requirement_spec.js @@ -191,20 +191,39 @@ function find_item_id(clicked_elt) { function standard_item_ajax_call(key, opt, other_data) { var data = { - action: "RequirementSpecTextBlock/ajax_" + key, + action: "RequirementSpecItem/ajax_" + key, requirement_spec_id: $('#requirement_spec_id').val(), id: find_item_id(opt.$trigger), current_content_type: $('#current_content_type').val(), current_content_id: $('#current_content_id').val() }; - console.log("I would normally POST the following now:"); - console.log(data); - // $.post("controller.pl", $.extend(data, other_data || {}), eval_json_result); + // console.log("I would normally POST the following now:"); + // console.log(data); + $.post("controller.pl", $.extend(data, other_data || {}), eval_json_result); return true; } function disable_edit_item_commands(key, opt) { - return false; // find_item_id(opt.$trigger) == undefined; + return find_item_id(opt.$trigger) == undefined; +} + +function submit_edit_item_form(id_base) { + var id = $('#' + id_base + '_id').val(); + var url = "controller.pl?" + $('#' + id_base + '_form').serialize(); + var data = { + action: 'RequirementSpecItem/ajax_' + (id ? 'update' : 'create'), + id: id, + form_prefix: id_base + }; + $.post(url, data, eval_json_result); + return true; +} + +function cancel_edit_item_form(form_id_base, hidden_id_base) { + var id = $('#' + form_id_base + '_id').val(); + $('#' + form_id_base + '_form').remove(); + if (id) + $('#' + hidden_id_base + '-' + id).show(); } diff --git a/locale/de/all b/locale/de/all index 44587beef..0db49e6f6 100755 --- a/locale/de/all +++ b/locale/de/all @@ -746,6 +746,7 @@ $self->{texts} = { 'Description (translation for #1)' => 'Beschreibung (Übersetzung für #1)', 'Description missing!' => 'Beschreibung fehlt.', 'Description must not be empty!' => 'Beschreibung darf nicht leer sein', + 'Description of #1' => 'Beschreibung von #1', 'Destination BIC' => 'Ziel-BIC', 'Destination IBAN' => 'Ziel-IBAN', 'Destination bin' => 'Ziellagerplatz', @@ -891,7 +892,6 @@ $self->{texts} = { 'Edit requirement spec status' => 'Pflichtenheftstatus bearbeiten', 'Edit requirement spec type' => 'Pflichtenhefttypen bearbeiten', 'Edit risk level' => 'Risikograd bearbeiten', - 'Edit section #1' => 'Abschnitt #1 bearbeiten', 'Edit templates' => 'Vorlagen bearbeiten', 'Edit text block' => 'Textblock bearbeiten', 'Edit text block \'#1\'' => 'Textblock \'#1\' bearbeiten', @@ -906,7 +906,6 @@ $self->{texts} = { 'Edit units' => 'Einheiten bearbeiten', 'Edit user signature' => 'Benutzersignatur bearbeiten', 'Editable' => 'Bearbeitbar', - 'Effort' => 'Aufwand', 'Either there are no open invoices, or you have already initiated bank transfers with the open amounts for those that are still open.' => 'Entweder gibt es keine offenen Rechnungen, oder es wurden bereits Überweisungen über die offenen Beträge aller offenen Rechnungen erstellt.', 'Element disabled' => 'Element deaktiviert', 'Employee' => 'Bearbeiter', @@ -1457,7 +1456,7 @@ $self->{texts} = { 'No requirement spec statuses has been created yet.' => 'Es wurden noch keine Pflichtenheftstatus angelegt.', 'No requirement spec type has been created yet.' => 'Es wurden noch keine Pflichtenhefttypen angelegt.', 'No risks level has been created yet.' => 'Es wurden noch keine Risikograde angelegt.', - 'No sections have been created so far.' => '', + 'No sections have been created so far.' => 'Bisher wurden noch keine Abschnitte angelegt.', 'No shipto selected to delete' => 'Keine Lieferadresse zum Löschen ausgewählt', 'No summary account' => 'Kein Sammelkonto', 'No text blocks have been created for this position.' => 'Für diese Position wurden noch keine Textblöcke angelegt.', @@ -2452,6 +2451,7 @@ $self->{texts} = { 'Three Options:' => 'Drei Optionen:', 'Time Format' => 'Uhrzeitformat', 'Time Tracking' => 'Zeiterfassung', + 'Time estimate' => 'Zeitschätzung', 'Time period for the analysis:' => 'Analysezeitraum:', 'Timestamp' => 'Uhrzeit', 'Title' => 'Titel', diff --git a/templates/webpages/requirement_spec/show.html b/templates/webpages/requirement_spec/show.html index 71c5b345f..0f12cd31c 100644 --- a/templates/webpages/requirement_spec/show.html +++ b/templates/webpages/requirement_spec/show.html @@ -34,72 +34,66 @@