RSS Git Download  Clone
Raw Blame History 10kB 190 lines
HTML rendered
<!-- Search Query -->
<p3xr-ng-accordion [title]="strings().page?.search?.title || 'Search'" accordionKey="search-query">
    <div content>
        <div class="p3xr-padding">
            @if (indexes.length === 0) {
                <div style="opacity: 0.5;">{{ strings().page?.search?.noIndex || 'No indexes found' }}</div>
            }

            @if (indexes.length > 0) {
                <div style="display: flex; align-items: center; gap: 8px;">
                    <mat-form-field class="md-block" style="flex: 1;">
                        <mat-label>{{ strings().page?.search?.index || 'Index' }}</mat-label>
                        <mat-select [(value)]="selectedIndex" (selectionChange)="offset = 0; indexInfo = null; loadIndexInfo()">
                            @for (idx of indexes; track idx) {
                                <mat-option [value]="idx">{{ idx }}</mat-option>
                            }
                        </mat-select>
                    </mat-form-field>
                    @if (!isReadonly && selectedIndex) {
                        <button mat-mini-fab class="btn-warn" (click)="dropIndex()" [matTooltip]="strings().page?.search?.dropIndex || 'Drop Index'" matTooltipPosition="above">
                            <mat-icon>delete</mat-icon>
                        </button>
                    }
                </div>

                <mat-form-field class="md-block">
                    <mat-label>{{ strings().page?.search?.query || 'Query' }}</mat-label>
                    <input matInput [(ngModel)]="query" (keydown.enter)="offset = 0; handleSearchEnter()" [disabled]="aiLoading" />
                </mat-form-field>

                <div style="margin-top: 8px; text-align: right;">
                    @if (isGtSm) {
                        <button mat-raised-button class="btn-primary p3xr-action-btn" type="button" (click)="handleSearchEnter()" [disabled]="aiLoading">
                            <mat-icon>search</mat-icon>
                            <span>{{ aiLoading ? (strings().label?.aiTranslating || 'Translating...') : (strings().page?.search?.title || 'Search') }}</span>
                        </button>
                    } @else {
                        <button mat-mini-fab class="btn-primary" type="button" (click)="handleSearchEnter()" [disabled]="aiLoading"
                                [matTooltip]="strings().page?.search?.title || 'Search'" matTooltipPosition="above">
                            <mat-icon>search</mat-icon>
                        </button>
                    }
                </div>
            }
        </div>
    </div>
</p3xr-ng-accordion>

<!-- Results -->
@if (searchDone && total === 0) {
    <br />
    <p3xr-ng-accordion [title]="(strings().page?.search?.results || 'Results') + ' (0)'" accordionKey="search-results">
        <div content>
            <div style="padding: 16px; opacity: 0.5;">{{ strings().label?.noResults || 'No results' }}</div>
        </div>
    </p3xr-ng-accordion>
}
@if (results.length > 0 || total > 0) {
    <br />
    <p3xr-ng-accordion [title]="(strings().page?.search?.results || 'Results') + ' (' + total + ')'" accordionKey="search-results">
        <div actions>
            @if (pages > 1) {
                <p3xr-ng-button (click)="pageAction('first'); $event.stopPropagation()" label="" mdIcon="skip_previous"></p3xr-ng-button>
                <p3xr-ng-button (click)="pageAction('prev'); $event.stopPropagation()" label="" mdIcon="keyboard_arrow_left"></p3xr-ng-button>
                <span style="font-size: 12px; opacity: 0.7;">{{ currentPage }} / {{ pages }}</span>
                <p3xr-ng-button (click)="pageAction('next'); $event.stopPropagation()" label="" mdIcon="keyboard_arrow_right"></p3xr-ng-button>
                <p3xr-ng-button (click)="pageAction('last'); $event.stopPropagation()" label="" mdIcon="skip_next"></p3xr-ng-button>
            }
        </div>
        <div content>
            <mat-list>
                @for (doc of results; track doc._key) {
                    <mat-list-item>
                        <div class="p3xr-settings-pair-row">
                            <div class="p3xr-settings-row-label">
                                <kbd class="p3xr-kbd p3xr-kbd-small">{{ doc._key }}</kbd>
                            </div>
                            <div class="p3xr-settings-row-value p3xr-mono" style="font-size: 12px;">
                                @for (field of getDocKeys(doc); track field) {
                                    <span>{{ field }}: {{ doc[field] }}</span>
                                    @if (!$last) { <span> · </span> }
                                }
                            </div>
                        </div>
                    </mat-list-item>
                    <mat-divider></mat-divider>
                }
            </mat-list>
        </div>
    </p3xr-ng-accordion>
}

<!-- Index Info — shown after first search -->
@if (selectedIndex && indexInfo) {
    <br />
    <p3xr-ng-accordion [title]="(strings().page?.search?.indexInfo || 'Index Info') + ': ' + selectedIndex" accordionKey="search-index-info">
        <div actions>
            @if (!isReadonly) {
                <p3xr-ng-button
                    (click)="dropIndex(); $event.stopPropagation()"
                    [label]="strings().page?.search?.dropIndex || 'Drop'"
                    mdIcon="delete">
                </p3xr-ng-button>
            }
        </div>
        <div content>
            @if (indexInfo) {
                <mat-list>
                    @for (key of getDocKeys(indexInfo); track key) {
                        <mat-list-item>
                            <div class="p3xr-settings-pair-row">
                                <div class="p3xr-settings-row-label">{{ key }}</div>
                                <div class="p3xr-settings-row-value p3xr-mono" style="font-size: 12px;">{{ indexInfo[key] | json }}</div>
                            </div>
                        </mat-list-item>
                        <mat-divider></mat-divider>
                    }
                </mat-list>
            }
        </div>
    </p3xr-ng-accordion>
}

<!-- Create Index -->
@if (!isReadonly) {
    <br />
    <p3xr-ng-accordion [title]="strings().page?.search?.createIndex || 'Create Index'" accordionKey="search-create-index">
        <div content>
            <div class="p3xr-padding">
                <mat-form-field class="md-block">
                    <mat-label>{{ strings().page?.search?.indexName || 'Index Name' }}</mat-label>
                    <input matInput [(ngModel)]="newIndexName" />
                </mat-form-field>

                <mat-form-field class="md-block">
                    <mat-label>{{ strings().page?.search?.prefix || 'Key Prefix (optional)' }}</mat-label>
                    <input matInput [(ngModel)]="newIndexPrefix" placeholder="e.g. doc:" />
                </mat-form-field>

                <div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
                    <strong>Schema</strong>
                    <button mat-mini-fab class="btn-primary" (click)="addField()" type="button">
                        <mat-icon>add</mat-icon>
                    </button>
                </div>

                @for (field of newIndexFields; track $index; let i = $index) {
                    <div class="p3xr-search-schema-row">
                        <mat-form-field style="flex: 1;">
                            <mat-label>{{ strings().page?.search?.fieldName || 'Field Name' }}</mat-label>
                            <input matInput [(ngModel)]="field.name" />
                        </mat-form-field>
                        <div class="p3xr-search-schema-type-row">
                            <mat-form-field style="width: 130px;">
                                <mat-label>{{ strings().label?.type || 'Type' }}</mat-label>
                                <mat-select [(value)]="field.type">
                                    <mat-option value="TEXT">TEXT</mat-option>
                                    <mat-option value="NUMERIC">NUMERIC</mat-option>
                                    <mat-option value="TAG">TAG</mat-option>
                                    <mat-option value="GEO">GEO</mat-option>
                                    <mat-option value="VECTOR">VECTOR</mat-option>
                                </mat-select>
                            </mat-form-field>
                            <button mat-mini-fab class="btn-warn" (click)="confirmRemoveField(i)" [disabled]="newIndexFields.length <= 1" type="button">
                                <mat-icon>remove</mat-icon>
                            </button>
                        </div>
                    </div>
                }

                <div style="margin-top: 8px; text-align: right;">
                    @if (isGtSm) {
                        <button mat-raised-button class="btn-accent p3xr-action-btn" type="button" (click)="createIndex()" [disabled]="!newIndexName.trim()">
                            <mat-icon>add</mat-icon>
                            <span>{{ strings().page?.search?.createIndex || 'Create Index' }}</span>
                        </button>
                    } @else {
                        <button mat-mini-fab class="btn-accent" type="button" (click)="createIndex()" [disabled]="!newIndexName.trim()"
                                [matTooltip]="strings().page?.search?.createIndex || 'Create Index'" matTooltipPosition="above">
                            <mat-icon>add</mat-icon>
                        </button>
                    }
                </div>
            </div>
        </div>
    </p3xr-ng-accordion>
}

<div style="height: 64px;"></div>