mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 07:08:55 +02:00
feat(react/widgets): port sql_result
This commit is contained in:
parent
4df94d1f20
commit
735e91e636
@ -11,21 +11,7 @@ import froca from "../services/froca.js";
|
||||
import linkService from "../services/link.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import type FNote from "../entities/fnote.js";
|
||||
|
||||
// TODO: Move somewhere else nicer.
|
||||
export type SqlExecuteResults = string[][][];
|
||||
|
||||
// TODO: Deduplicate with server.
|
||||
interface SqlExecuteResponse {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
results: SqlExecuteResults;
|
||||
}
|
||||
|
||||
// TODO: Deduplicate with server.
|
||||
interface CreateChildrenResponse {
|
||||
note: FNote;
|
||||
}
|
||||
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
|
||||
|
||||
export default class Entrypoints extends Component {
|
||||
constructor() {
|
||||
|
@ -42,6 +42,7 @@ import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
||||
import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
||||
import SearchResult from "../widgets/search_result.jsx";
|
||||
import GlobalMenu from "../widgets/buttons/global_menu.jsx";
|
||||
import SqlResults from "../widgets/sql_result.js";
|
||||
|
||||
export default class DesktopLayout {
|
||||
|
||||
@ -140,7 +141,7 @@ export default class DesktopLayout {
|
||||
.child(new NoteDetailWidget())
|
||||
.child(new NoteListWidget(false))
|
||||
.child(<SearchResult />)
|
||||
.child(new SqlResultWidget())
|
||||
.child(<SqlResults />)
|
||||
.child(<ScrollPadding />)
|
||||
)
|
||||
.child(new ApiLogWidget())
|
||||
|
7
apps/client/src/widgets/sql_result.css
Normal file
7
apps/client/src/widgets/sql_result.css
Normal file
@ -0,0 +1,7 @@
|
||||
.sql-result-widget {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.sql-console-result-container td {
|
||||
white-space: preserve;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
import type { EventData } from "../components/app_context.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="sql-result-widget">
|
||||
<style>
|
||||
.sql-result-widget {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.sql-console-result-container td {
|
||||
white-space: preserve;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sql-query-no-rows alert alert-info" style="display: none;">
|
||||
${t("sql_result.no_rows")}
|
||||
</div>
|
||||
|
||||
<div class="sql-console-result-container"></div>
|
||||
</div>`;
|
||||
|
||||
export default class SqlResultWidget extends NoteContextAwareWidget {
|
||||
|
||||
private $resultContainer!: JQuery<HTMLElement>;
|
||||
private $noRowsAlert!: JQuery<HTMLElement>;
|
||||
|
||||
isEnabled() {
|
||||
return this.note && this.note.mime === "text/x-sqlite;schema=trilium" && super.isEnabled();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
this.$resultContainer = this.$widget.find(".sql-console-result-container");
|
||||
this.$noRowsAlert = this.$widget.find(".sql-query-no-rows");
|
||||
}
|
||||
|
||||
async sqlQueryResultsEvent({ ntxId, results }: EventData<"sqlQueryResults">) {
|
||||
if (!this.isNoteContext(ntxId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$noRowsAlert.toggle(results.length === 1 && results[0].length === 0);
|
||||
this.$resultContainer.toggle(results.length > 1 || results[0].length > 0);
|
||||
|
||||
this.$resultContainer.empty();
|
||||
|
||||
for (const rows of results) {
|
||||
if (typeof rows === "object" && !Array.isArray(rows)) {
|
||||
// inserts, updates
|
||||
this.$resultContainer
|
||||
.empty()
|
||||
.show()
|
||||
.append($("<pre>").text(JSON.stringify(rows, null, "\t")));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rows.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const $table = $('<table class="table table-striped">');
|
||||
this.$resultContainer.append($table);
|
||||
|
||||
const result = rows[0];
|
||||
const $row = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
$row.append($("<th>").text(key));
|
||||
}
|
||||
|
||||
$table.append($row);
|
||||
|
||||
for (const result of rows) {
|
||||
const $row = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
$row.append($("<td>").text(result[key]));
|
||||
}
|
||||
|
||||
$table.append($row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
apps/client/src/widgets/sql_result.tsx
Normal file
62
apps/client/src/widgets/sql_result.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { SqlExecuteResults } from "@triliumnext/commons";
|
||||
import { useNoteContext, useTriliumEvent } from "./react/hooks";
|
||||
import "./sql_result.css";
|
||||
import { useState } from "preact/hooks";
|
||||
import Alert from "./react/Alert";
|
||||
import { t } from "../services/i18n";
|
||||
|
||||
export default function SqlResults() {
|
||||
const { note, ntxId } = useNoteContext();
|
||||
const [ results, setResults ] = useState<SqlExecuteResults>();
|
||||
|
||||
useTriliumEvent("sqlQueryResults", ({ ntxId: eventNtxId, results }) => {
|
||||
if (eventNtxId !== ntxId) return;
|
||||
setResults(results);
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="sql-result-widget">
|
||||
{note?.mime === "text/x-sqlite;schema=trilium" && (
|
||||
results?.length === 1 && Array.isArray(results[0]) && results[0].length === 0 ? (
|
||||
<Alert type="info">
|
||||
{t("sql_result.no_rows")}
|
||||
</Alert>
|
||||
) : (
|
||||
<div class="sql-console-result-container">
|
||||
{results?.map(rows => {
|
||||
// inserts, updates
|
||||
if (typeof rows === "object" && !Array.isArray(rows)) {
|
||||
return <pre>{JSON.stringify(rows, null, "\t")}</pre>
|
||||
}
|
||||
|
||||
// selects
|
||||
return <SqlResultTable rows={rows} />
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SqlResultTable({ rows }: { rows: object[] }) {
|
||||
if (!rows.length) return;
|
||||
|
||||
return (
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{Object.keys(rows[0]).map(key => <th>{key}</th>)}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{rows.map(row => (
|
||||
<tr>
|
||||
{Object.values(row).map(cell => <td>{cell}</td>)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
@ -12,7 +12,7 @@ import ValidationError from "../../errors/validation_error.js";
|
||||
import blobService from "../../services/blob.js";
|
||||
import type { Request } from "express";
|
||||
import type BBranch from "../../becca/entities/bbranch.js";
|
||||
import type { AttributeRow, DeleteNotesPreview, MetadataResponse } from "@triliumnext/commons";
|
||||
import type { AttributeRow, CreateChildrenResponse, DeleteNotesPreview, MetadataResponse } from "@triliumnext/commons";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@ -123,7 +123,7 @@ function createNote(req: Request) {
|
||||
return {
|
||||
note,
|
||||
branch
|
||||
};
|
||||
} satisfies CreateChildrenResponse;
|
||||
}
|
||||
|
||||
function updateNoteData(req: Request) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AttachmentRow, AttributeRow, NoteType } from "./rows.js";
|
||||
import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from "./rows.js";
|
||||
|
||||
type Response = {
|
||||
success: true,
|
||||
@ -220,3 +220,17 @@ export type BacklinksResponse = ({
|
||||
noteId: string;
|
||||
excerpts: string[]
|
||||
})[];
|
||||
|
||||
|
||||
export type SqlExecuteResults = (object[] | object)[];
|
||||
|
||||
export interface SqlExecuteResponse {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
results: SqlExecuteResults;
|
||||
}
|
||||
|
||||
export interface CreateChildrenResponse {
|
||||
note: NoteRow;
|
||||
branch: BranchRow;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user