%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2025 Best Practical Solutions, LLC
%#                                          <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%#
%# LICENSE:
%#
%# This work is made available to you under the terms of Version 2 of
%# the GNU General Public License. A copy of that license should have
%# been provided with this software, but in any event can be snarfed
%# from www.gnu.org.
%#
%# This work is distributed in the hope that it will be useful, but
%# WITHOUT ANY WARRANTY; without even the implied warranty of
%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%# General Public License for more details.
%#
%# You should have received a copy of the GNU General Public License
%# along with this program; if not, write to the Free Software
%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
%# 02110-1301 or visit their web page on the internet at
%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
%#
%#
%# CONTRIBUTION SUBMISSION POLICY:
%#
%# (The following paragraph is not intended to limit the rights granted
%# to you to modify and distribute this software under the terms of
%# the GNU General Public License and is only of importance to you if
%# you choose to contribute your changes and enhancements to the
%# community by submitting them to Best Practical Solutions, LLC.)
%#
%# By intentionally submitting any modifications, corrections or
%# derivatives to this work, or any other work intended for use with
%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
%# you are the copyright holder for those contributions and you grant
%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
%# royalty-free, perpetual, license to use, copy, create derivative
%# works based on those contributions, and sublicense and distribute
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
<&|/Widgets/TitleBox,
    title => $title,
    title_raw => $title_raw,
    title_href => $query_link_url.$QueryString,
    icons_ref => \@icon_links,
    hideable => $hideable,
    class => 'fullwidth',
    htmx_get => RT->Config->Get('WebPath') . ( $session{CurrentUser}->Privileged ? '' : '/SelfService' ) . '/Views/Component/SavedSearch?SavedSearch=' . $SavedSearch . $htmx_query_args,
    htmx_load => $HTMXLoad,
    htmx_id => 'rt-savedsearch-' . $search->Id,
&>
% if ( $show_display_mode_alert ) {
<div class="alert alert-info alert-dismissible fade show mt-2 mb-0 py-2 px-3" role="alert">
  <strong><&|/l&>This is a temporary preview.</&></strong>
%   if ( $can_modify_search ) {
  <&|/l_unsafe, '<a href="' . $m->interp->apply_escapes($customize, 'h') . '">' . loc('edit the saved search') . '</a>' &>To make it permanent, [_1] and click Options to save it as the default mode.</&>
%   } else {
  <&|/l&>To make it permanent, ask the owner to edit the saved search or create a personal copy.</&>
%   }
  <button type="button" class="btn-close py-2 pe-1 my-1 btn btn-sm" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
% }
<& $query_display_component, hideable => $hideable, %$ProcessedSearchArg, ShowNavigation => 0, Class => $class, HasResults => $HasResults, PreferOrderBy => 1, SavedSearchObj => $search &>
% if ( !$m->notes('render-dashboard-email') ) {
  <div class="refresh-text">
    <small class="text-body-secondary"><&|/l&>Last loaded</&> <% $loaded_date->AsString %>.</small>
% if ( $refresh_seconds ) {
    <small class="text-body-secondary"><&|/l, RT::Date->new($session{CurrentUser})->DurationAsString($refresh_seconds) &>Refreshing every [_1].</&></small>
% } elsif ( $paused_refresh_seconds ) {
    <small class="text-body-secondary"><&|/l, RT::Date->new($session{CurrentUser})->DurationAsString($paused_refresh_seconds) &>Auto-refresh (every [_1]) paused in preview mode.</&></small>
% }
  </div>
  <& /Widgets/Spinner &>
% }
</&>
<%init>
my $search;
my $user = $session{'CurrentUser'}->UserObj;
my $SearchArg;
my $customize;
my $query_display_component = '/Elements/CollectionList';
my $query_link_url = RT->Config->Get('WebPath').'/Search/Results.html';
my $class = 'RT::Tickets';
my $refresh_seconds;
my $loaded_date;
my %event;

if ($SavedSearch) {
    $search = RT::SavedSearch->new( $session{'CurrentUser'} );
    $search->Load($SavedSearch);

    if ( $search->Id ) {
        # $container_object is undef if it's another user's personal saved
        # search. We need to explicitly exclude this case as
        # CurrentUserHasRight doesn't handle that.
        if ( $search->CurrentUserCanSee ) {
            if ( $search->Disabled && !$IgnoreMissing ) {
                $m->out( loc( "Saved search [_1] is disabled", $m->interp->apply_escapes( $search->Name ) ) );
                return;
            }

            $SearchArg = $search->Content || {};
        }
        else {
            RT->Logger->debug( "User "
                    . $session{CurrentUser}->Name
                    . " does not have rights to view saved search: "
                    . $search->__Value('Name')
                    . "($SavedSearch)" );
            return;
        }
    }
    else {
        $m->out(loc("Saved search [_1] not found", $m->interp->apply_escapes($SavedSearch, 'h'))) unless $IgnoreMissing;
        return;
    }

    if ( $search->PrincipalId == RT->System->Id ) {
        $SearchArg = $user->Preferences( $search, $search->Content );
    }

    $SearchArg->{'SavedSearchId'} ||= $SavedSearch;
    $SearchArg->{'SearchType'} = $search->Type;

    if ( my $mode = $Override{SearchDisplayMode} || $SearchArg->{'SearchDisplayMode'} ) {
        if ( $mode ne 'CollectionList' ) {
            $query_display_component = '/Search/Elements/' . $mode;
            $ShowCount = 0;
        }
    }

    if ( $SearchArg->{SearchType} eq 'TicketTransaction' ) {
        $class = $SearchArg->{Class} = 'RT::Transactions';
        $customize
            = RT->Config->Get('WebPath')
            . '/Search/Build.html?'
            . $m->comp( '/Elements/QueryString', SavedSearchLoad => $SavedSearch, Class => 'RT::Transactions' );
        $ShowCount = RT->Config->Get('TransactionShowSearchResultCount')->{'RT::Ticket'};
    }
    elsif ( $SearchArg->{SearchType} eq 'Asset' ) {
        $class = $SearchArg->{Class} = 'RT::Assets';
        $customize
            = RT->Config->Get('WebPath')
            . '/Search/Build.html?'
            . $m->comp( '/Elements/QueryString', SavedSearchLoad => $SavedSearch, Class => 'RT::Assets' );
        $ShowCount = RT->Config->Get('AssetShowSearchResultCount');
    }
    elsif ( $SearchArg->{SearchType} ne 'Ticket' ) {

        my $type = $SearchArg->{'SearchType'};
        if ( $type =~ /Chart/ ) {
            $SearchArg->{'SavedChartSearchId'} ||= $SavedSearch;
            $class = $SearchArg->{Class} if $SearchArg->{Class};
            $type = 'Chart';
            $customize
                = RT->Config->Get('WebPath')
                . '/Search/Chart.html?'
                . $m->comp( '/Elements/QueryString', SavedSearchLoad => $SavedSearch );
        }

        # XXX: dispatch to different handler here
        $query_display_component
            = '/Search/Elements/' . $type;
        $query_link_url = RT->Config->Get('WebPath') . "/Search/$type.html";
        $ShowCount = 0;
    } elsif ($ShowCustomize) {
        if ( $search->PrincipalId == RT->System->Id ) {
            $customize = RT->Config->Get('WebPath') . '/Prefs/Search.html?'
                . $m->comp( '/Elements/QueryString', id => $SavedSearch );
        }
        else {
            $customize = RT->Config->Get('WebPath') . '/Search/Build.html?'
                . $m->comp( '/Elements/QueryString', SavedSearchLoad => $SavedSearch );
        }
    }

    $loaded_date = RT::Date->new( $session{CurrentUser} );
    $loaded_date->Set( Format => 'Unix', Value => $m->{'rt_base_time'}->[0] );

    if ( $SearchArg->{'SearchRefreshInterval'} && $SearchArg->{'SearchRefreshInterval'} > 0 ) {
        $refresh_seconds = $SearchArg->{'SearchRefreshInterval'};
    }

}

# ProcessedSearchArg is a search with overridings, but for link we use
# orginal search's poperties
my $ProcessedSearchArg = $SearchArg;
$ProcessedSearchArg = { %$SearchArg, %Override } if keys %Override;

# Check if we should show the display mode alert
my $show_display_mode_alert = 0;
my $can_modify_search = 0;
my $paused_refresh_seconds = 0;

if ( $search && $Override{SearchDisplayMode} ) {
    # Get the saved display mode (defaulting to CollectionList)
    my $saved_mode = $SearchArg->{'SearchDisplayMode'} || 'CollectionList';
    my $current_mode = $Override{SearchDisplayMode};

    # Only show alert if the override is different from the saved mode
    if ( $saved_mode ne $current_mode ) {
        $show_display_mode_alert = 1;

        # Check if user can modify this search
        if ( $search->CurrentUserCanModify ) {
            $can_modify_search = 1;
        }

        # Disable auto-refresh when in preview mode
        if ( $refresh_seconds ) {
            $paused_refresh_seconds = $refresh_seconds;
            $refresh_seconds = 0;
        }
    }
}

my $htmx_query_args = '';

$m->callback(
    %ARGS,
    CallbackName  => 'ModifySearch',
    OriginalSearch => $SearchArg,
    Search         => $ProcessedSearchArg,
    HTMXQueryArgsRef => \$htmx_query_args,
);

foreach ( $SearchArg, $ProcessedSearchArg ) {
    $_->{'Format'} ||= '';
    $_->{'Query'} ||= '';

    # extract-message-catalog would "$1", so we avoid quotes for loc calls
    $_->{'Format'} =~ s/__loc\(["']?(\w+)["']?\)__/my $f = "$1"; loc($f)/ge;
}

my $QueryString = '?' . QueryString( ShortenSearchQuery(%$SearchArg) );

my $title_raw;

# Both not-lazy load and requests from /Views/ have $HTMXLoad set to false.
if ( $ShowCount && !$HTMXLoad ) {
    my $collection = $class->new( $session{'CurrentUser'} );
    my $query;
    if ( $class eq 'RT::Transactions' ) {
        $query = join ' AND ', "ObjectType = '$ProcessedSearchArg->{ObjectType}'",
            $ProcessedSearchArg->{Query} ? "($ProcessedSearchArg->{Query})" : ();
    }
    else {
        $query = $ProcessedSearchArg->{Query};
    }

    # Add selected user to notes if it was submitted
    if ( $ARGS{SavedSearchSelectedUserName} ) {
        my $user = RT::User->new( $session{CurrentUser} );
        my ( $ret, $msg ) = $user->Load( $ARGS{SavedSearchSelectedUserName} );

        if ( $ret and $user->Id ) {
            $m->notes->{SavedSearchSelectedUserId}   = $user->Id;
            $m->notes->{SavedSearchSelectedUserName} = $user->Name;
        }
        else {
            RT->Logger->error( "Unable to load user in dashboard for SelectedUserName "
                    . $ARGS{SavedSearchSelectedUserName}
                    . " : $msg" );
        }
    }

    $collection->FromSQL($query);
    if ( $ProcessedSearchArg->{OrderBy} ) {
        my @order_by;
        my @order;
        if ( $ProcessedSearchArg->{OrderBy} =~ /\|/ ) {
            @order_by = split /\|/, $ProcessedSearchArg->{OrderBy};
            @order    = split /\|/, $ProcessedSearchArg->{Order};
        }
        else {
            @order_by = $ProcessedSearchArg->{OrderBy};
            @order    = $ProcessedSearchArg->{Order};
        }
        @order_by = grep length, @order_by;
        $collection->OrderByCols( map { { FIELD => $order_by[$_], ORDER => $order[$_] } } ( 0 .. $#order_by ) );
    }
    $collection->RowsPerPage( $ProcessedSearchArg->{Rows} ) if $ProcessedSearchArg->{Rows};
    $collection->CombineSearchAndCount(1);

    my $count = $collection->CountAll();

    my $title;
    if ( $class eq 'RT::Transactions' ) {
        $title = loc('(Found [quant,_1,transaction,transactions])', $count);
    }
    elsif ( $class eq 'RT::Assets' ) {
        $title = loc('(Found [quant,_1,asset,assets])', $count);
    }
    else {
        $title = loc('(Found [quant,_1,ticket,tickets])', $count);
    }
    $title_raw = '<span class="results-count">' . $title . '</span>';

    # don't repeat the search in CollectionList
    $ProcessedSearchArg->{Collection} = $collection;
    $ProcessedSearchArg->{TotalFound} = $count;
}

if ( exists $Override{'Rows'} ) {
    $htmx_query_args .= "&Rows=" . $Override{'Rows'};
}
if ( defined $Override{'SavedSearchSelectedUserName'} ) {
    $htmx_query_args .= "&SavedSearchSelectedUserName=" . $Override{'SavedSearchSelectedUserName'};
}

my $title = loc(RT::SavedSearch->EscapeDescription($search->Description), $ProcessedSearchArg->{'Rows'});

# If the request is a manual reload from clicking the button, don't
# set the refresh trigger on that link element.
if ( $m->request_path =~ m{^/Views/} && !$m->request_args->{'Reload'} ) {
    $event{triggerChanged} = 'every ' . $refresh_seconds . 's[checkRefreshState(this)]' if $refresh_seconds;
    $event{widgetTitleChanged}   = $title . ( $title_raw // '' );
    $r->headers_out->{'HX-Trigger'} = JSON( \%event, ascii => 1, );
}

if ( $m->request_args->{'Reload'} ) {
    $r->headers_out->{'HX-Trigger'} = JSON( { actionsChanged => [ loc( '[_1] reloaded', $title ) ] }, ascii => 1, );
}

my @icon_links;

push @icon_links,
    {
        icon_name    => 'arrow-clockwise',
        tooltip_text => loc('Reload'),
        icon_href    => '#',
        htmx_get     => RT->Config->Get('WebPath')
        . ( $session{CurrentUser}->Privileged ? '' : '/SelfService' )
        . '/Views/Component/SavedSearch?SavedSearch='
        . $SavedSearch
        . $htmx_query_args
        . '&Reload=1',
        htmx_target => '#rt-savedsearch-' . $search->Id,
        htmx_indicator => '#rt-savedsearch-' . $search->Id,
    };

# Add info icon for calendar display mode to show date type colors
if ( $SearchArg && $SearchArg->{'SearchDisplayMode'} && $SearchArg->{'SearchDisplayMode'} eq 'Calendar' ) {
    push @icon_links,
        {
            icon_name    => 'info',
            tooltip_text => loc('Date Type Colors'),
            icon_href    => 'javascript:void(0)',
            modal        => "#calendar-date-colors-modal",
        };
}

if ( $SearchArg && ( $SearchArg->{'SearchType'} // '' ) eq 'Ticket' ) {
    push @icon_links,
        {   icon_name      => 'grid',
            tooltip_text   => loc('Display Mode'),
            icon_href      => '#',
            dropdown_items => [
                {   item_href  => '#',
                    item_id    => '',
                    item_class => 'display-mode-table',
                    item_text  => loc('Table'),
                    htmx_get     => RT->Config->Get('WebPath')
                    . ( $session{CurrentUser}->Privileged ? '' : '/SelfService' )
                    . '/Views/Component/SavedSearch?SavedSearch='
                    . $SavedSearch
                    . $htmx_query_args
                    . '&SearchDisplayMode=CollectionList',
                    htmx_target => '#rt-savedsearch-' . $search->Id,
                    htmx_indicator => '#rt-savedsearch-' . $search->Id,
                },
                {   item_href  => '#',
                    item_id    => '',
                    item_class => 'display-mode-calendar',
                    item_text  => loc('Calendar'),
                    htmx_get     => RT->Config->Get('WebPath')
                    . ( $session{CurrentUser}->Privileged ? '' : '/SelfService' )
                    . '/Views/Component/SavedSearch?SavedSearch='
                    . $SavedSearch
                    . $htmx_query_args
                    . '&SearchDisplayMode=Calendar',
                    htmx_target => '#rt-savedsearch-' . $search->Id,
                    htmx_indicator => '#rt-savedsearch-' . $search->Id,
                },
            ],
        };
}

if ( $customize ) {
    push @icon_links,
        {
            icon_name => 'gear',
            tooltip_text => loc('Edit'),
            icon_href => $customize,
        };
}

</%init>
<%ARGS>
$Name           => undef
$SavedSearch    => undef
%Override       => ()
$IgnoreMissing  => undef
$hideable       => 1
$ShowCustomize  => 1
$ShowCount      => RT->Config->Get('ShowSearchResultCount')
$HasResults     => undef
$HTMXLoad       => undef # Pass 1 to render with htmx load enabled
</%ARGS>
