#package Reaction::UI::ViewPort::TimeRangeCollection; # # Marked commented out because unused and unmaintained. Should probably # be turned into a complex viewport example later instead --mst # #use Reaction::Class; #use Reaction::Types::DateTime; #use Moose::Util::TypeConstraints (); #use DateTime::Event::Recurrence; #use aliased 'Reaction::UI::ViewPort::Field::String'; #use aliased 'Reaction::UI::ViewPort::Field::DateTime'; #use aliased 'Reaction::UI::ViewPort::Field::HiddenArray'; #use aliased 'Reaction::UI::ViewPort::Field::TimeRange'; # #class TimeRangeCollection is 'Reaction::UI::ViewPort', which { # # #has '+layout' => (default => 'timerangecollection'); # # has '+column_order' => ( # default => sub{[ qw/ time_from time_to pattern repeat_from repeat_to / ]}, # ); # # has time_from => ( # isa => 'Reaction::UI::ViewPort::Field::DateTime', # is => 'rw', lazy_build => 1, # ); # # has time_to => ( # isa => 'Reaction::UI::ViewPort::Field::DateTime', # is => 'rw', lazy_build => 1, # ); # # has repeat_from => ( # isa => 'Reaction::UI::ViewPort::Field::DateTime', # is => 'rw', lazy_build => 1, # ); # # has repeat_to => ( # isa => 'Reaction::UI::ViewPort::Field::DateTime', # is => 'rw', lazy_build => 1, # ); # # has pattern => ( # isa => 'Reaction::UI::ViewPort::Field::String', # # valid_values => [ qw/none daily weekly monthly/ ], # is => 'rw', lazy_build => 1, # ); # # has range_vps => (isa => 'ArrayRef', is => 'rw', lazy_build => 1,); # # has max_range_vps => (isa => 'Int', is => 'rw', lazy_build => 1,); # # has error => ( # isa => 'Str', # is => 'rw', # required => 0, # ); # # has field_names => ( # isa => 'ArrayRef', is => 'rw', # lazy_build => 1, clearer => 'clear_field_names', # ); # # has _field_map => ( # isa => 'HashRef', is => 'rw', init_arg => 'fields', # clearer => '_clear_field_map', # predicate => '_has_field_map', # lazy_build => 1, # ); # # has on_next_callback => ( # isa => 'CodeRef', # is => 'rw', # predicate => 'has_on_next_callback', # ); # # implements fields => as { shift->_field_map }; # # implements _build_range_vps => as { [] }; # # implements spanset => as { # my ($self) = @_; # my $spanset = DateTime::SpanSet->empty_set; # $spanset = $spanset->union($_->value) for @{$self->range_vps}; # return $spanset; # }; # # implements range_strings => as { # my ($self) = @_; # return [ map { $_->value_string } @{$self->range_vps} ]; # }; # # implements remove_range_vp => as { # my ($self, $to_remove) = @_; # $self->range_vps([ grep { $_ != $to_remove } @{$self->range_vps} ]); # $self->_clear_field_map; # $self->clear_field_names; # }; # # implements add_range_vp => as { # my ($self) = @_; # if ($self->can_add) { # $self->_clear_field_map; # $self->clear_field_names; # my @span_info = ( # $self->time_from->value, # $self->time_to->value, # (map { $_->has_value ? $_->value : '' } # map { $self->$_ } qw/repeat_from repeat_to/), # $self->pattern->value, # ); # my $encoded_spanset = join ',', @span_info; # my $args = { # value_string => $encoded_spanset, # parent => $self # }; # my $count = scalar(@{$self->range_vps}); # my $field = $self->_build_simple_field(TimeRange, 'range-'.$count, $args); # my $d = DateTime::Format::Duration->new( pattern => '%s' ); # if ($d->format_duration( $self->spanset->intersection($field->value)->duration ) > 0) { # # XXX - Stop using the stash here? # $self->ctx->stash->{warning} = 'Warning: Most recent time range overlaps '. # 'with existing time range in this booking.'; # } # #warn "encoded spanset = $encoded_spanset\n"; # #warn "current range = ".join(', ', (@{$self->range_vps}))."\n"; # push(@{$self->range_vps}, $field); # } # }; # # implements _build_field_map => as { # my ($self) = @_; # my %map; # foreach my $field (@{$self->range_vps}) { # $map{$field->name} = $field; # } # foreach my $name (@{$self->column_order}) { # $map{$name} = $self->$name; # } # return \%map; # }; # # implements _build_field_names => as { # my ($self) = @_; # return [ # (map { $_->name } @{$self->range_vps}), # @{$self->column_order} # ]; # }; # # implements can_add => as { # my ($self) = @_; # my $error; # if ($self->time_to->has_value && $self->time_from->has_value) { # my $time_to = $self->time_to->value; # my $time_from = $self->time_from->value; # # my ($pattern, $repeat_from, $repeat_to) = ('','',''); # $pattern = $self->pattern->value if $self->pattern->has_value; # $repeat_from = $self->repeat_from->value if $self->repeat_from->has_value; # $repeat_to = $self->repeat_to->value if $self->repeat_to->has_value; # # my $duration = $time_to - $time_from; # if ($time_to < $time_from) { # $error = 'Please make sure that the Time To is after the Time From.'; # } elsif ($time_to == $time_from) { # $error = 'Your desired booking slot is too small.'; # } elsif ($pattern && $pattern ne 'none') { # my %pattern = (hourly => [ hours => 1 ], # daily => [ days => 1 ], # weekly => [ days => 7 ], # monthly => [ months => 1 ]); # my $pattern_comp = DateTime::Duration->compare( # $duration, DateTime::Duration->new( @{$pattern{$pattern}} ) # ); # if (!$repeat_to || !$repeat_from) { # $error = 'Please make sure that you enter a valid range for the '. # 'repetition period.'; # } elsif ($time_to == $time_from) { # $error = 'Your desired repetition period is too short.'; # } elsif ($repeat_to && ($repeat_to < $repeat_from)) { # $error = 'Please make sure that the Repeat To is after the Repeat From.'; # } elsif ( ( ($pattern eq 'hourly') && ($pattern_comp > 0) ) || # ( ($pattern eq 'daily') && ($pattern_comp > 0) ) || # ( ($pattern eq 'weekly') && ($pattern_comp > 0) ) || # ( ($pattern eq 'monthly') && ($pattern_comp > 0) ) ) { # $error = "Your repetition pattern ($pattern) is too short for your ". # "desired booking length."; # } # } # } else { # $error = 'Please complete both the Time To and Time From fields.'; # } # $self->error($error); # return !defined($error); # }; # # implements _build_simple_field => as { # my ($self, $class, $name, $args) = @_; # return $class->new( # name => $name, # location => join('-', $self->location, 'field', $name), # ctx => $self->ctx, # %$args # ); # }; # # implements _build_time_to => as { # my ($self) = @_; # return $self->_build_simple_field(DateTime, 'time_to', {}); # }; # # implements _build_time_from => as { # my ($self) = @_; # return $self->_build_simple_field(DateTime, 'time_from', {}); # }; # # implements _build_repeat_to => as { # my ($self) = @_; # return $self->_build_simple_field(DateTime, 'repeat_to', {}); # }; # # implements _build_repeat_from => as { # my ($self) = @_; # return $self->_build_simple_field(DateTime, 'repeat_from', {}); # }; # # implements _build_pattern => as { # my ($self) = @_; # return $self->_build_simple_field(String, 'pattern', {}); # }; # # implements next => as { # $_[0]->on_next_callback->(@_); # }; # # override accept_events => sub { # my $self = shift; # ('add_range_vp', ($self->has_on_next_callback ? ('next') : ()), super()); # }; # # override child_event_sinks => sub { # my ($self) = @_; # return ((grep { ref($_) =~ 'Hidden' } values %{$self->_field_map}), # (grep { ref($_) !~ 'Hidden' } values %{$self->_field_map}), # super()); # }; # # override apply_events => sub { # my ($self, $ctx, $events) = @_; # # # auto-inflate range fields based on number from hidden field # # my $max = $events->{$self->location.':max_range_vps'}; # my @range_vps = map { # TimeRange->new( # name => "range-$_", # location => join('-', $self->location, 'field', 'range', $_), # ctx => $self->ctx, # parent => $self, # ) # } ($max ? (0 .. $max - 1) : ()); # $self->range_vps(\@range_vps); # $self->_clear_field_map; # $self->clear_field_names; # # # call original event handling # # super(); # # # repack range VPs in case of deletion # # my $prev_idx = -1; # # foreach my $vp (@{$self->range_vps}) { # my $cur_idx = ($vp->name =~ m/range-(\d+)/); # if (($cur_idx - $prev_idx) > 1) { # $cur_idx--; # my $name = "range-${cur_idx}"; # $vp->name($name); # $vp->location(join('-', $self->location, 'field', $name)); # } # $prev_idx = $cur_idx; # } # }; # #}; # #1; # #=head1 NAME # #Reaction::UI::ViewPort::TimeRangeCollection # #=head1 SYNOPSIS # # my $trc = $self->push_viewport(TimeRangeCollection, # layout => 'avail_search_form', # on_apply_callback => $search_callback, # name => 'TRC', # ); # #=head1 DESCRIPTION # #=head1 ATTRIBUTES # #=head2 can_add # #=head2 column_order # #=head2 error # #=head2 field_names # #=head2 fields # #=head2 layout # #=head2 pattern # #Typically either: none, daily, weekly or monthly # #=head2 max_range_vps # #=head2 range_vps # #=head2 repeat_from # #A DateTime field. # #=head2 repeat_to # #A DateTime field. # #=head2 time_from # #A DateTime field. # #=head2 time_to # #A DateTime field. # #=head1 METHODS # #=head2 spanset # #Returns: $spanset consisting of all the TimeRange spans combined # #=head2 range_strings # #Returns: ArrayRef of Str consisting of the value_strings of all TimeRange #VPs # #=head2 remove_range_vp # #Arguments: $to_remove # #=head2 add_range_vp # #Arguments: $to_add # #=head2 _build_simple_field # #Arguments: $class, $name, $args #where $class is an object, $name is a scalar and $args is a hashref # #=head2 next # #=head2 on_next_callback # #=head2 clear_field_names # #=head2 child_event_sinks # #=head1 SEE ALSO # #=head2 L # #=head2 L # #=head2 L # #=head2 L # #=head1 AUTHORS # #See L for authors. # #=head1 LICENSE # #See L for the license. # #=cut 1;