Add document route

This commit is contained in:
Stanislav Romanov 2022-08-30 14:59:24 +03:00
parent 38d86a3785
commit c62db432c7
15 changed files with 320 additions and 210 deletions

View file

@ -0,0 +1 @@
export { PencilOutline as default } from 'ember-mdi';

View file

@ -1,66 +1,3 @@
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { Magnify, Table, MapOutline } from 'ember-mdi';
export default class AdminIndexesItemDocumentsController extends Controller {
@tracked q = null;
@tracked limit = 20;
@tracked page = 1;
@tracked dataView = 'table';
@tracked sort;
queryParams = ['q', 'page', 'limit', 'sort'];
Magnify = Magnify;
// key: view name, value: icon
views = {
table: Table,
map: MapOutline,
};
get fromHits() {
return 1 + this.model.data.offset;
}
get toHits() {
return this.model.data.offset + this.model.data.hits.length;
}
get sortedFields() {
const fields = Object.keys(this.model.stats.fieldDistribution);
const primaryKey = this.model.index.primaryKey ?? 'id';
const sortedArray = fields
.filter((item) => item !== primaryKey)
.sort((a, b) => {
if (a > b || a.charAt(0) === '_') {
return 1;
}
if (a < b) {
return -1;
}
return 0;
});
return [primaryKey].concat(sortedArray);
}
@action
searchTextChanged(value) {
this.q = value === '' ? null : value;
}
@action
onSort(newSort) {
if (`${newSort}:desc` === this.sort) {
this.sort = null;
} else if (`${newSort}:asc` === this.sort) {
this.sort = `${newSort}:desc`;
} else {
this.sort = `${newSort}:asc`;
}
}
}
export default class AdminIndexesItemDocumentsController extends Controller {}

View file

@ -0,0 +1,66 @@
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { Magnify, Table, MapOutline } from 'ember-mdi';
export default class AdminIndexesItemDocumentsIndexController extends Controller {
@tracked q = null;
@tracked limit = 20;
@tracked page = 1;
@tracked dataView = 'table';
@tracked sort;
queryParams = ['q', 'page', 'limit', 'sort'];
Magnify = Magnify;
// key: view name, value: icon
views = {
table: Table,
map: MapOutline,
};
get fromHits() {
return 1 + this.model.data.offset;
}
get toHits() {
return this.model.data.offset + this.model.data.hits.length;
}
get sortedFields() {
const fields = Object.keys(this.model.stats.fieldDistribution);
const primaryKey = this.model.index.primaryKey ?? 'id';
const sortedArray = fields
.filter((item) => item !== primaryKey)
.sort((a, b) => {
if (a > b || a.charAt(0) === '_') {
return 1;
}
if (a < b) {
return -1;
}
return 0;
});
return [primaryKey].concat(sortedArray);
}
@action
searchTextChanged(value) {
this.q = value === '' ? null : value;
}
@action
onSort(newSort) {
if (`${newSort}:desc` === this.sort) {
this.sort = null;
} else if (`${newSort}:asc` === this.sort) {
this.sort = `${newSort}:desc`;
} else {
this.sort = `${newSort}:asc`;
}
}
}

21
app/models/document.js Normal file
View file

@ -0,0 +1,21 @@
import query from '../utils/query';
export default class Document {
constructor(data = {}) {
for (const key in data) {
this[key] = data[key];
}
}
// get path() {
// return `${this.#index.path}/documents/${this.documentId}`;
// }
// get isNew() {
// return Boolean(!this.documentId);
// }
// get documentId() {
// return this[this.#index.primaryKey];
// }
}

View file

@ -1,22 +1,16 @@
import query from '../utils/query';
// import { TrackedArray } from 'tracked-built-ins';
// import { trackedFunction } from 'ember-resources/util/function';
import Document from './document';
export default class Index {
// stats = trackedFunction(this, () => query(`${this.indexPath}/stats`));
// displayedAttributes = trackedFunction(this, () =>
// query(`${this.indexPath}/settings/displayed-attributes`).then(
// (arr) => new TrackedArray(arr)
// )
// );
constructor(data = {}) {
for (const key in data) {
this[key] = data[key];
}
}
get indexPath() {
get path() {
return `indexes/${this.uid}`;
}
@ -24,6 +18,45 @@ export default class Index {
return Boolean(!this.uid);
}
getSortableAttributes() {
return query(`${this.path}/settings/sortable-attributes`);
}
getStats() {
return query(`${this.path}/stats`);
}
// getDocuments(searchParams) {
// return query(`${this.path}/documents`, {
// body: searchParams,
// });
// }
getDocument(uid) {
return query(`${this.path}/documents/${uid}`).then(
(result) => new Document(this, result)
);
}
search(q, options = {}) {
options.q = q;
return query(`${this.path}/search`, {
method: 'POST',
body: JSON.stringify(options),
});
}
save(data) {
const path = this.isNew ? 'indexes' : this.path;
const method = this.isNew ? 'POST' : 'PATCH';
return query(path, {
method: method,
body: JSON.stringify(data),
});
}
// get allAttributesAreDisplayed() {
// return (
// this.displayedAttributes.value &&
@ -33,73 +66,53 @@ export default class Index {
// }
// getDisplayedAttributes() {
// return query(`${this.indexPath}/settings/displayed-attributes`);
// return query(`${this.path}/settings/displayed-attributes`);
// }
// updateDisplayedAttributes(value) {
// return query(`${this.indexPath}/settings/displayed-attributes`, {
// return query(`${this.path}/settings/displayed-attributes`, {
// method: 'POST',
// body: JSON.stringify(value),
// });
// }
// resetDisplayedAttributes() {
// return query(`${this.indexPath}/settings/displayed-attributes`, {
// return query(`${this.path}/settings/displayed-attributes`, {
// method: 'DELETE',
// });
// }
// getSearchableAttributes() {
// return query(`${this.indexPath}/settings/searchable-attributes`);
// return query(`${this.path}/settings/searchable-attributes`);
// }
// updateSearchableAttributes(value) {
// return query(`${this.indexPath}/settings/searchable-attributes`, {
// return query(`${this.path}/settings/searchable-attributes`, {
// method: 'POST',
// body: JSON.stringify(value),
// });
// }
// resetSearchableAttributes() {
// return query(`${this.indexPath}/settings/searchable-attributes`, {
// return query(`${this.path}/settings/searchable-attributes`, {
// method: 'DELETE',
// });
// }
getSortableAttributes() {
return query(`${this.indexPath}/settings/sortable-attributes`);
}
getStats() {
return query(`${this.indexPath}/stats`);
}
// getSettings() {
// return query(`${this.indexPath}/settings`);
// }
search(q, options = {}) {
options.q = q;
return query(`${this.indexPath}/search`, {
method: 'POST',
body: JSON.stringify(options),
});
}
// get fields() {
// return this.stats.value
// ? Object.keys(this.stats.value.fieldDistribution)
// : [];
// }
save(data) {
const path = this.isNew ? 'indexes' : this.indexPath;
const method = this.isNew ? 'POST' : 'PATCH';
// getSettings() {
// return query(`${this.path}/settings`);
// }
return query(path, {
method: method,
body: JSON.stringify(data),
});
}
// stats = trackedFunction(this, () => query(`${this.path}/stats`));
// displayedAttributes = trackedFunction(this, () =>
// query(`${this.path}/settings/displayed-attributes`).then(
// (arr) => new TrackedArray(arr)
// )
// );
}

View file

@ -13,7 +13,9 @@ Router.map(function () {
this.route('settings');
this.route('info');
this.route('edit');
this.route('documents');
this.route('documents', function () {
this.route('document', { path: '/:uid' });
});
});
this.route('new');
});

View file

@ -1,45 +1,3 @@
import Route from '@ember/routing/route';
import { hash } from 'rsvp';
export default class AdminIndexesItemDocumentsRoute extends Route {
queryParams = {
q: {
refreshModel: true,
},
limit: {
refreshModel: true,
},
page: {
refreshModel: true,
},
sort: {
refreshModel: true,
},
};
async model({ q, page, limit, sort }) {
const offset = limit * --page;
const index = this.modelFor('admin.indexes.item');
const options = { offset, limit };
if (sort) {
options.sort = [sort];
}
return hash({
index,
data: index.search(q, options),
sortableAttributes: index.getSortableAttributes(),
stats: index.getStats(),
});
}
resetController(controller, isExiting) {
if (isExiting) {
controller.q = null;
controller.page = 1;
}
window.scrollTo(0, 0);
}
}
export default class AdminIndexesItemDocumentsRoute extends Route {}

View file

@ -0,0 +1,13 @@
import Route from '@ember/routing/route';
import { hash } from 'rsvp';
export default class AdminIndexesItemDocumentsDocumentRoute extends Route {
model({ uid }) {
const index = this.modelFor('admin.indexes.item');
return hash({
index,
document: index.getDocument(uid),
});
}
}

View file

@ -0,0 +1,45 @@
import Route from '@ember/routing/route';
import { hash } from 'rsvp';
export default class AdminIndexesItemDocumentsIndexRoute extends Route {
queryParams = {
q: {
refreshModel: true,
},
limit: {
refreshModel: true,
},
page: {
refreshModel: true,
},
sort: {
refreshModel: true,
},
};
async model({ q, page, limit, sort }) {
const offset = limit * --page;
const index = this.modelFor('admin.indexes.item');
const options = { offset, limit };
if (sort) {
options.sort = [sort];
}
return hash({
index,
data: index.search(q, options),
sortableAttributes: index.getSortableAttributes(),
stats: index.getStats(),
});
}
resetController(controller, isExiting) {
if (isExiting) {
controller.q = null;
controller.page = 1;
}
window.scrollTo(0, 0);
}
}

View file

@ -1,59 +1 @@
<InputText
@placeholder='Search'
@prefixIcon={{this.Magnify}}
@value={{this.q}}
@onChange={{this.searchTextChanged}}
/>
{{!-- <div class='flex items-center mb-4'>
<div class='flex items-center ml-auto'>
<Icons::Check class='mr-1' @size='18' />
Hits:
{{@model.data.estimatedTotalHits}}
<Icons::ClockOutline class='ml-6 mr-1' @size='18' />
Time spent:
{{@model.data.processingTimeMs}}
ms
</div>
</div> --}}
{{#if (gt @model.data.estimatedTotalHits 0)}}
<RoyalTable
@data={{@model.data.hits}}
@sortingProp={{this.sort}}
@onSort={{this.onSort}}
class='w-full'
as |Row data|
>
<Row as |Column|>
{{#each-in @model.stats.fieldDistribution as |field|}}
<Column
@name={{capitalize field}}
@prop={{field}}
@sortable={{includes field @model.sortableAttributes}}
>
{{#let (get data field) as |value|}}
{{if (eq (type-of value) 'object') (stringify value) value}}
{{/let}}
</Column>
{{/each-in}}
</Row>
</RoyalTable>
<div class='flex items-center'>
{{this.fromHits}}
—
{{this.toHits}}
of
{{@model.data.estimatedTotalHits}}
<Pagination
@count={{@model.data.estimatedTotalHits}}
@current={{this.page}}
@perPage={{@model.data.limit}}
class='ml-auto'
/>
</div>
{{else}}
No results found
{{/if}}
{{outlet}}

View file

@ -0,0 +1,68 @@
<InputText
@placeholder='Search'
@prefixIcon={{this.Magnify}}
@value={{this.q}}
@onChange={{this.searchTextChanged}}
/>
{{!-- <div class='flex items-center mb-4'>
<div class='flex items-center ml-auto'>
<Icons::Check class='mr-1' @size='18' />
Hits:
{{@model.data.estimatedTotalHits}}
<Icons::ClockOutline class='ml-6 mr-1' @size='18' />
Time spent:
{{@model.data.processingTimeMs}}
ms
</div>
</div> --}}
{{#if (gt @model.data.estimatedTotalHits 0)}}
<RoyalTable
@data={{@model.data.hits}}
@sortingProp={{this.sort}}
@onSort={{this.onSort}}
class='w-full'
as |Row data|
>
<Row as |Column|>
{{#each-in @model.stats.fieldDistribution as |field|}}
<Column
@name={{capitalize field}}
@prop={{field}}
@sortable={{includes field @model.sortableAttributes}}
>
{{#let (get data field) as |value|}}
{{if (eq (type-of value) 'object') (stringify value) value}}
{{/let}}
</Column>
{{/each-in}}
<Column>
<LinkTo
@route='admin.indexes.item.documents.document'
@models={{array @model.index.uid (get data @model.index.primaryKey)}}
>
<Icons::PencilOutline class='mr-1' />
Edit
</LinkTo>
</Column>
</Row>
</RoyalTable>
<div class='flex items-center'>
{{this.fromHits}}
—
{{this.toHits}}
of
{{@model.data.estimatedTotalHits}}
<Pagination
@count={{@model.data.estimatedTotalHits}}
@current={{this.page}}
@perPage={{@model.data.limit}}
class='ml-auto'
/>
</div>
{{else}}
No results found
{{/if}}

View file

@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupTest } from 'meiliadmin/tests/helpers';
module(
'Unit | Controller | admin/indexes/item/documents/index',
function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup(
'controller:admin/indexes/item/documents/index'
);
assert.ok(controller);
});
}
);

View file

@ -0,0 +1,16 @@
import { module, test } from 'qunit';
import { setupTest } from 'meiliadmin/tests/helpers';
module(
'Unit | Route | admin/indexes/item/documents/document',
function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup(
'route:admin/indexes/item/documents/document'
);
assert.ok(route);
});
}
);

View file

@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from 'meiliadmin/tests/helpers';
module('Unit | Route | admin/indexes/item/documents/index', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:admin/indexes/item/documents/index');
assert.ok(route);
});
});