9205099f14
The previous round crammed every control into the widget header as one flex row, which read as cluttered. Restore the stock Portainer two-row shape: - header bar: the "Logs" title on the left; Search + Copy + Download logs right-aligned in the header's transclude slot; - a single clean horizontal toolbar in the widget body: Since / Lines / Wrap lines / Display timestamps (no longer stacked form-groups); - the log <pre> pane below, unchanged. Auto-refresh and the line-selection controls stay removed (already gone from the controller). Template-only change; no controller edits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
4.2 KiB
HTML
71 lines
4.2 KiB
HTML
<div class="row">
|
|
<div class="col-sm-12">
|
|
<rd-widget>
|
|
<!-- Top bar: the "Logs" title sits on the left while the Search input and
|
|
the Copy / Download buttons are projected into the header's default
|
|
transclude slot, where they render right-aligned. -->
|
|
<rd-widget-header icon="file" title-text="Logs">
|
|
<form class="form-horizontal !m-0">
|
|
<div class="flex flex-wrap items-center justify-end gap-x-4 gap-y-2">
|
|
<div class="flex items-center gap-x-2" style="min-width: 160px">
|
|
<label for="logs_search" class="control-label !w-auto whitespace-nowrap !p-0 text-left"> Search </label>
|
|
<input class="form-control flex-1" type="text" name="logs_search" ng-model="$ctrl.state.search" placeholder="Filter..." />
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-1" ng-if="$ctrl.state.copySupported">
|
|
<button
|
|
class="btn btn-primary btn-sm"
|
|
ng-click="$ctrl.copy()"
|
|
ng-disabled="($ctrl.state.filteredLogs.length === 1 && !$ctrl.state.filteredLogs[0].line) || !$ctrl.state.filteredLogs.length"
|
|
><pr-icon icon="'copy'" class-name="'space-right'"></pr-icon>Copy</button
|
|
>
|
|
<button class="btn btn-primary btn-sm" type="button" ng-click="$ctrl.downloadLogs()" style="margin-left: 0" data-cy="download-logs-button"
|
|
><pr-icon icon="'download'"></pr-icon> Download logs</button
|
|
>
|
|
<span>
|
|
<pr-icon id="refreshRateChange" icon="'check'" mode="'success'" style="display: none"></pr-icon>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</rd-widget-header>
|
|
<rd-widget-body>
|
|
<!-- Toolbar row: a single clean, left-aligned horizontal row holding the
|
|
remaining controls (Since / Lines / Wrap lines / Display timestamps),
|
|
instead of stacked form-groups. -->
|
|
<form class="form-horizontal !m-0">
|
|
<div class="flex flex-wrap items-center gap-x-4 gap-y-2">
|
|
<div class="flex items-center gap-x-2">
|
|
<label for="logs_since" class="control-label !w-auto whitespace-nowrap !p-0 text-left"> Since </label>
|
|
<select class="form-control !w-auto" ng-model="$ctrl.sinceTimestamp" id="logs_since">
|
|
<option selected value="">All logs</option>
|
|
<option ng-repeat="dt in $ctrl.state.availableSinceDatetime" ng-value="dt.value" title="{{ dt.value }}">{{ dt.desc }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="flex items-center gap-x-2">
|
|
<label for="lines_count" class="control-label !w-auto whitespace-nowrap !p-0 text-left"> Lines </label>
|
|
<input class="form-control !w-24" type="number" name="lines_count" ng-model="$ctrl.lineCount" placeholder="Lines..." />
|
|
</div>
|
|
<por-switch-field label-class="'!w-auto'" checked="$ctrl.state.wrapLines" label="'Wrap lines'" on-change="($ctrl.handleLogsWrapLinesChange)"></por-switch-field>
|
|
<por-switch-field
|
|
label-class="'!w-auto'"
|
|
checked="$ctrl.displayTimestamps"
|
|
label="'Display timestamps'"
|
|
on-change="($ctrl.handleDisplayTimestampsChange)"
|
|
></por-switch-field>
|
|
</div>
|
|
</form>
|
|
</rd-widget-body>
|
|
</rd-widget>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="height: 54%">
|
|
<div class="col-sm-12" style="height: 100%">
|
|
<pre ng-class="{ wrap_lines: $ctrl.state.wrapLines }" class="log_viewer" scroll-glue="$ctrl.state.autoScroll">
|
|
<div ng-repeat="log in $ctrl.state.filteredLogs track by log.id" class="line" ng-if="log.line"><p class="inner_line"><span ng-repeat="span in log.spans track by $index" ng-style="{ 'color': span.fgColor, 'background-color': span.bgColor, 'font-weight': span.fontWeight }">{{ span.text }}</span></p></div>
|
|
<div ng-if="!$ctrl.state.filteredLogs.length && $ctrl.state.search" class="line"><p class="inner_line">No log line matching the '{{ $ctrl.state.search }}' filter</p></div>
|
|
<div ng-if="!$ctrl.state.filteredLogs.length && !$ctrl.state.search" class="line"><p class="inner_line">No logs available</p></div>
|
|
</pre>
|
|
</div>
|
|
</div>
|