pgkiln 0.88.0 → 0.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/guide/03-builder.md +53 -25
- package/package.json +1 -1
- package/public/builder.css +57 -3
- package/public/builder.js +66 -0
- package/public/palette.js +8 -1
- package/public/worksheet.css +39 -4
- package/public/worksheet.js +15 -1
- package/src/builder/designer.ts +2 -2
- package/src/builder/gallery.ts +5 -5
- package/src/builder/home.ts +71 -60
- package/src/builder/i18n/de.ts +22 -0
- package/src/builder/i18n/en.ts +22 -0
- package/src/builder/i18n/es.ts +22 -0
- package/src/builder/i18n/fr.ts +22 -0
- package/src/builder/i18n/it.ts +22 -0
- package/src/builder/i18n/ja.ts +22 -0
- package/src/builder/i18n/ko.ts +22 -0
- package/src/builder/i18n/nl.ts +22 -0
- package/src/builder/i18n/pt.ts +22 -0
- package/src/builder/i18n/zh-cn.ts +22 -0
- package/src/builder/i18n/zh-tw.ts +22 -0
- package/src/builder/routes.ts +21 -10
- package/src/builder/sql.ts +2 -2
- package/src/builder/ui.ts +47 -33
- package/src/builder/worksheet.ts +33 -24
- package/src/builder/workspace-home.ts +17 -0
- package/src/session.ts +2 -0
package/src/builder/home.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { FastifyInstance, FastifyReply } from 'fastify';
|
|
|
2
2
|
import { owner } from '../db.ts';
|
|
3
3
|
import { html, raw, type Raw } from '../html.ts';
|
|
4
4
|
import { icon } from '../icons.ts';
|
|
5
|
-
import { APP_COLORS, BASE, csrf, developer, input, region, send, shell, type Req } from './ui.ts';
|
|
5
|
+
import { APP_COLORS, BASE, csrf, developer, input, pageHeader, region, send, shell, type Req } from './ui.ts';
|
|
6
6
|
import { crossAppLinks } from './cross-app.ts';
|
|
7
7
|
import type { Session } from '../session.ts';
|
|
8
8
|
import { isAdmin } from './locks.ts';
|
|
@@ -53,16 +53,6 @@ const lockedText = (tx: BuilderTranslate, by: string) => tx('ab.locked_by', { us
|
|
|
53
53
|
const appIcon = (a: { id: number; name: string }, cls = '') =>
|
|
54
54
|
html`<span class="app-icon app-color-${a.id % APP_COLORS}${cls ? ` ${cls}` : ''}" aria-hidden="true">${a.name.slice(0, 1).toUpperCase()}</span>`;
|
|
55
55
|
|
|
56
|
-
/** The four tiles at the top of the App Builder. */
|
|
57
|
-
const tiles = (tx: BuilderTranslate) => html`<nav class="ab-tiles" aria-label="${tx('rail.app_builder')}">
|
|
58
|
-
${([
|
|
59
|
-
['create', 'plus', tx('ab.tile.create'), tx('ab.tile.create_text')],
|
|
60
|
-
['import', 'upload', tx('ab.tile.import'), tx('ab.tile.import_text')],
|
|
61
|
-
['dashboard', 'activity', tx('ab.tile.dashboard'), tx('ab.tile.dashboard_text')],
|
|
62
|
-
['utilities', 'settings', tx('ab.tile.utilities'), tx('ab.tile.utilities_text')],
|
|
63
|
-
] as const).map(([path, ic, title, text]) => html`<a class="ab-tile" href="${BASE}/${path}"><span class="ab-tile-icon">${icon(ic)}</span><strong>${title}</strong><span>${text}</span></a>`)}
|
|
64
|
-
</nav>`;
|
|
65
|
-
|
|
66
56
|
/** The side column: About, Recent, Tasks. */
|
|
67
57
|
function aside(tx: BuilderTranslate, recent: { id: number; name: string }[], about: string, tasks: [string, string][]) {
|
|
68
58
|
return html`<aside class="ab-side" aria-label="${tx('ab.aside')}">
|
|
@@ -73,10 +63,9 @@ function aside(tx: BuilderTranslate, recent: { id: number; name: string }[], abo
|
|
|
73
63
|
</aside>`;
|
|
74
64
|
}
|
|
75
65
|
|
|
66
|
+
// (UX-5) what the header doesn't offer already (Create and Import are there)
|
|
76
67
|
const TASKS: [BuilderKey, string][] = [
|
|
77
|
-
['ab.task.create', `${BASE}/create`],
|
|
78
68
|
['ab.task.create_file', `${BASE}/create/file`],
|
|
79
|
-
['ab.task.import', `${BASE}/import`],
|
|
80
69
|
['ab.task.users', `${BASE}/users`],
|
|
81
70
|
['ab.task.providers', `${BASE}/users/providers`],
|
|
82
71
|
['rail.sql_workshop', `${BASE}/sql`],
|
|
@@ -174,9 +163,13 @@ export async function homeRoutes(app: FastifyInstance) {
|
|
|
174
163
|
html`<a class="tb-btn" href="${link({ view: v })}" title="${label}"${view === v ? raw(' aria-current="true"') : ''}>${icon(ic)}<span class="sr-only">${label}</span></a>`;
|
|
175
164
|
const main = html`<div class="ab-home">
|
|
176
165
|
<div class="ab-main">
|
|
177
|
-
|
|
166
|
+
${pageHeader(tx('rail.app_builder'), {
|
|
167
|
+
primary: [`${BASE}/create`, tx('ab.tile.create'), 'plus'],
|
|
168
|
+
secondary: [[`${BASE}/import`, tx('ab.tile.import'), 'upload']],
|
|
169
|
+
more: [[`${BASE}/dashboard`, tx('ab.tile.dashboard'), 'activity'], [`${BASE}/utilities`, tx('rail.admin'), 'settings']],
|
|
170
|
+
moreLabel: tx('ws.more'),
|
|
171
|
+
})}
|
|
178
172
|
${s.workspace?.message ? html`<div class="alert alert-info ab-message" role="status">${icon('bell')}<div><strong>${s.workspace.name}</strong><p>${s.workspace.message}</p></div></div>` : ''}
|
|
179
|
-
${tiles(tx)}
|
|
180
173
|
<div class="ab-toolbar">
|
|
181
174
|
<form class="ab-search" method="get" action="${BASE}/apps" role="search">
|
|
182
175
|
<label class="sr-only" for="ab-q">${tx('ab.search_label')}</label>${icon('search')}
|
|
@@ -190,13 +183,8 @@ export async function homeRoutes(app: FastifyInstance) {
|
|
|
190
183
|
<div class="menu-panel" role="menu">
|
|
191
184
|
<div class="menu-section"><span class="small muted">${tx('ab.sort_by')}</span>
|
|
192
185
|
${(Object.keys(SORTS) as Sort[]).map((k) => html`<a role="menuitem" href="${link({ sort: k })}"${sort === k ? raw(' aria-current="true"') : ''}>${tx(`ab.sort.${k}`)}</a>`)}</div>
|
|
193
|
-
<div class="menu-section menu-links">
|
|
194
|
-
<a role="menuitem" href="${BASE}/import">${icon('upload')} ${tx('ab.task.import')}</a>
|
|
195
|
-
<a role="menuitem" href="${BASE}/dashboard">${icon('activity')} ${tx('ab.ws_dashboard')}</a>
|
|
196
|
-
</div>
|
|
197
186
|
</div></details>
|
|
198
187
|
<span class="ab-count muted" aria-live="polite">${rows.length === total.n ? tx(total.n === 1 ? 'ab.count_one' : 'ab.count', { n: total.n }) : tx('ab.count_of', { n: rows.length, total: total.n })}</span>
|
|
199
|
-
<a class="btn btn-hot ab-create" href="${BASE}/create">${tx('ab.tile.create')}</a>
|
|
200
188
|
</div>
|
|
201
189
|
${list}
|
|
202
190
|
</div>
|
|
@@ -271,49 +259,72 @@ export async function homeRoutes(app: FastifyInstance) {
|
|
|
271
259
|
});
|
|
272
260
|
|
|
273
261
|
// ---------------------------------------------------------------- workspace utilities
|
|
262
|
+
// (UX-4) Administration: the workspace utilities in sections, with a filter; the rail's Administration entry (UX-3)
|
|
274
263
|
app.get(`${BASE}/utilities`, async (req: Req, reply) => {
|
|
275
264
|
const s = await developer(req, reply);
|
|
276
265
|
if (!s) return;
|
|
277
266
|
const tx = tr(s);
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
267
|
+
const admin = await isAdmin(s.username);
|
|
268
|
+
const wsAdmin = !!s.workspace?.admin || (!!s.workspace && (await workspaceAdmin(s.username, s.workspace.id)));
|
|
269
|
+
const ws = s.workspace;
|
|
270
|
+
type Tile = [href: string, ic: string, title: string, text: string | Raw] | null;
|
|
271
|
+
const tile = (t: Tile) =>
|
|
272
|
+
t ? html`<li><a class="adm-tile" href="${t[0]}"><span class="adm-tile-head">${icon(t[1])}<strong>${t[2]}</strong></span>${t[3] ? html`<span class="adm-tile-text">${t[3]}</span>` : ''}</a></li>` : '';
|
|
273
|
+
const section = (id: string, title: string, tiles: Tile[]) => {
|
|
274
|
+
const shown = tiles.filter(Boolean);
|
|
275
|
+
return shown.length ? html`<section class="adm-section" aria-labelledby="adm-${id}"><h2 id="adm-${id}">${title}</h2><ul class="adm-tiles">${shown.map(tile)}</ul></section>` : '';
|
|
276
|
+
};
|
|
277
|
+
const main = html`<div class="adm">
|
|
278
|
+
<div class="title-row"><h1>${tx('rail.admin')}</h1>
|
|
279
|
+
<div class="adm-filter" hidden><label class="sr-only" for="adm-q">${tx('wu.filter')}</label>
|
|
280
|
+
<input id="adm-q" type="search" placeholder="${tx('wu.filter')}" autocomplete="off" data-adm-filter></div></div>
|
|
281
|
+
<p class="u-mt0 adm-lede"><a href="https://github.com/pgkiln/pgkiln/blob/main/docs/guide/03-builder.md#workspaces" target="_blank" rel="noopener">${tx('wu.learn_more')}</a></p>
|
|
282
|
+
${section('people', tx('wu.sec.people'), [
|
|
283
|
+
[`${BASE}/users`, 'user', tx('wu.users'), tx('wu.users_text')],
|
|
284
|
+
[`${BASE}/developers`, 'users', tx('wu.developers'), tx('wu.developers_text')],
|
|
285
|
+
[`${BASE}/users/providers`, 'key', tx('wu.providers'), tx('wu.providers_text')],
|
|
286
|
+
[`${BASE}/users/directories`, 'org', tx('wu.ldap'), tx('wu.ldap_text')],
|
|
287
|
+
[`${BASE}/users#account-settings`, 'shield', tx('wu.password_policy'), tx('wu.password_policy_text')],
|
|
288
|
+
])}
|
|
289
|
+
${section('connections', tx('wu.sec.connections'), [
|
|
290
|
+
[`${BASE}/remote-servers`, 'server', tx('wu.servers'), tx('wu.servers_text')],
|
|
291
|
+
admin ? [`${BASE}/remote-databases`, 'database', tx('wu.remote_dbs'), tx('wu.remote_dbs_text')] : null,
|
|
292
|
+
[`${BASE}/workspace-credentials`, 'key', tx('wu.credentials'), tx('wu.credentials_text')],
|
|
293
|
+
[`${BASE}/rest-catalogs`, 'globe', tx('wu.catalogs'), tx('wu.catalogs_text')],
|
|
294
|
+
admin ? [`${BASE}/mail`, 'send', tx('wu.mail'), tx('wu.mail_text')] : null,
|
|
295
|
+
wsAdmin ? [`${BASE}/mail-queue`, 'inbox', tx('admin.mail_queue'), ''] : null,
|
|
296
|
+
wsAdmin ? [`${BASE}/network`, 'globe', tx('admin.network'), ''] : null,
|
|
297
|
+
])}
|
|
298
|
+
${section('data', tx('wu.sec.data'), [
|
|
299
|
+
admin ? [`${BASE}/ai`, 'bolt', tx('wu.ai'), tx('wu.ai_text')] : null,
|
|
300
|
+
[`${BASE}/vector-providers`, 'search', tx('wu.vectors'), tx('wu.vectors_text')],
|
|
301
|
+
[`${BASE}/ui-defaults`, 'sliders-horizontal', tx('wu.ui_defaults'), tx('wu.ui_defaults_text')],
|
|
302
|
+
[`${BASE}/workspace-files`, 'file', tx('wu.files'), tx('wu.files_text', { tag: '#WORKSPACE_FILES#' })],
|
|
303
|
+
[`${BASE}/icons`, 'image', tx('wu.icons'), tx('wu.icons_text')],
|
|
304
|
+
])}
|
|
305
|
+
${section('operations', tx('wu.sec.operations'), [
|
|
306
|
+
[`${BASE}/app-groups`, 'folder', tx('wu.app_groups'), tx('wu.app_groups_text')],
|
|
307
|
+
[`${BASE}/backups`, 'archive', tx('wu.backups'), tx('wu.backups_text')],
|
|
308
|
+
wsAdmin ? [`${BASE}/workspace-export`, 'download', tx('wu.export'), tx('wu.export_text')] : null,
|
|
309
|
+
[`${BASE}/dashboard`, 'activity', tx('wu.dashboard'), tx('wu.dashboard_text')],
|
|
310
|
+
admin ? [`${BASE}/telemetry`, 'activity', tx('wu.otel'), tx('wu.otel_text')] : null,
|
|
311
|
+
])}
|
|
312
|
+
${ws && wsAdmin ? section('workspace', tx('wu.sec.workspace', { name: ws.name }), [
|
|
313
|
+
[`${BASE}/workspaces/${ws.id}`, 'layers', tx('admin.ws_devs'), tx('wu.workspace_text')],
|
|
314
|
+
[`${BASE}/workspaces/${ws.id}#service`, 'bell', tx('admin.ws_message'), ''],
|
|
315
|
+
[`${BASE}/workspaces/${ws.id}#preferences`, 'sliders-horizontal', tx('admin.ws_prefs'), ''],
|
|
316
|
+
[`${BASE}/workspaces/${ws.id}#links`, 'external-link', tx('admin.ext_links'), ''],
|
|
317
|
+
[`${BASE}/workspaces/${ws.id}/utilization`, 'chart', tx('admin.utilization'), ''],
|
|
318
|
+
]) : ''}
|
|
319
|
+
${admin ? section('instance', tx('wu.sec.instance'), [
|
|
320
|
+
[`${BASE}/instance`, 'settings', tx('wu.instance'), tx('wu.instance_text')],
|
|
321
|
+
[`${BASE}/workspaces`, 'layers', tx('wu.workspaces'), tx('wu.workspaces_text')],
|
|
322
|
+
s.instanceAdmin ? [`${BASE}/db-users`, 'key', tx('admin.db_users'), ''] : null,
|
|
323
|
+
[`${BASE}/installation`, 'history', tx('wu.installation'), tx('wu.installation_text')],
|
|
324
|
+
]) : ''}
|
|
325
|
+
<section class="adm-section" aria-labelledby="adm-reports"><h2 id="adm-reports">${tx('wu.cross_app')}</h2><ul class="ab-links ab-tasks adm-reports">${crossAppLinks()}</ul></section>
|
|
326
|
+
<p class="adm-none muted" hidden>${tx('wu.filter_none')}</p>
|
|
327
|
+
</div>`;
|
|
328
|
+
return send(reply, s, shell(s, tx('rail.admin'), [[tx('rail.admin')]], main));
|
|
318
329
|
});
|
|
319
330
|
}
|
package/src/builder/i18n/de.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const de: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': 'Erneut laden nach (Stunden)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': '1 bis 168. Die Seiten werden nach dieser Zeit beim ersten Seitenaufruf erneut geladen, sofern eine Verbindung besteht.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': 'Erneut laden nach: eine ganze Zahl von Stunden zwischen 1 und 168.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'Weitere Aktionen',
|
|
7521
|
+
'ws.suggest': 'Vorschläge',
|
|
7522
|
+
'ws.suggest_title': 'Vorschläge an der Cursorposition (Strg+Leertaste)',
|
|
7523
|
+
'ws.check': 'Prüfen',
|
|
7524
|
+
'ws.check_title': 'Das SQL planen, ohne es auszuführen, wie der Advisor',
|
|
7525
|
+
'ws.key.indent': 'Einrücken (Esc, dann Tab: nächstes Feld)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} zuerst im Suchpfad',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'Beschriftungen anzeigen',
|
|
7529
|
+
'rail.labels_hide': 'Beschriftungen ausblenden',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'Hilfsprogramme filtern',
|
|
7532
|
+
'wu.filter_none': 'Nichts entspricht dem Filter.',
|
|
7533
|
+
'wu.sec.people': 'Personen und Anmeldung',
|
|
7534
|
+
'wu.sec.connections': 'Verbindungen',
|
|
7535
|
+
'wu.sec.data': 'Daten und KI',
|
|
7536
|
+
'wu.sec.operations': 'Betrieb',
|
|
7537
|
+
'wu.sec.workspace': 'Arbeitsbereich {name}',
|
|
7538
|
+
'wu.sec.instance': 'Instanz',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': 'Erfordert Aufmerksamkeit',
|
|
7519
7541
|
};
|
package/src/builder/i18n/en.ts
CHANGED
|
@@ -7520,4 +7520,26 @@ export const en = {
|
|
|
7520
7520
|
'pwa.prefetch_hours': 'Fetch them again after (hours)',
|
|
7521
7521
|
'pwa.prefetch_hours_help': '1 to 168. The pages are fetched again on the first page opened after this time, while there is a connection.',
|
|
7522
7522
|
'pwa.prefetch_hours_range': 'Fetch them again after: a whole number of hours from 1 to 168.',
|
|
7523
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7524
|
+
'ws.more': 'More actions',
|
|
7525
|
+
'ws.suggest': 'Suggest',
|
|
7526
|
+
'ws.suggest_title': 'Suggestions at the cursor (Ctrl+Space)',
|
|
7527
|
+
'ws.check': 'Check',
|
|
7528
|
+
'ws.check_title': 'Plan the SQL without running it, as the Advisor does',
|
|
7529
|
+
'ws.key.indent': 'Indent (Esc, then Tab: next field)',
|
|
7530
|
+
'ws.search_path_plain': '{schema} first in the search path',
|
|
7531
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7532
|
+
'rail.labels_show': 'Show labels',
|
|
7533
|
+
'rail.labels_hide': 'Hide labels',
|
|
7534
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7535
|
+
'wu.filter': 'Filter the utilities',
|
|
7536
|
+
'wu.filter_none': 'Nothing matches the filter.',
|
|
7537
|
+
'wu.sec.people': 'People and sign-in',
|
|
7538
|
+
'wu.sec.connections': 'Connections',
|
|
7539
|
+
'wu.sec.data': 'Data and AI',
|
|
7540
|
+
'wu.sec.operations': 'Operations',
|
|
7541
|
+
'wu.sec.workspace': 'Workspace {name}',
|
|
7542
|
+
'wu.sec.instance': 'Instance',
|
|
7543
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7544
|
+
'wh.attention': 'Needs attention',
|
|
7523
7545
|
} as const;
|
package/src/builder/i18n/es.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const es: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': 'Volver a obtenerlas después de (horas)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': 'De 1 a 168. Las páginas se vuelven a obtener en la primera página abierta después de este tiempo, mientras haya conexión.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': 'Volver a obtenerlas después de: un número entero de horas entre 1 y 168.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'Más acciones',
|
|
7521
|
+
'ws.suggest': 'Sugerir',
|
|
7522
|
+
'ws.suggest_title': 'Sugerencias en el cursor (Ctrl+Espacio)',
|
|
7523
|
+
'ws.check': 'Comprobar',
|
|
7524
|
+
'ws.check_title': 'Planificar el SQL sin ejecutarlo, como el Advisor',
|
|
7525
|
+
'ws.key.indent': 'Sangrar (Esc y luego Tab: siguiente campo)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} primero en la ruta de búsqueda',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'Mostrar etiquetas',
|
|
7529
|
+
'rail.labels_hide': 'Ocultar etiquetas',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'Filtrar las utilidades',
|
|
7532
|
+
'wu.filter_none': 'Nada coincide con el filtro.',
|
|
7533
|
+
'wu.sec.people': 'Personas e inicio de sesión',
|
|
7534
|
+
'wu.sec.connections': 'Conexiones',
|
|
7535
|
+
'wu.sec.data': 'Datos e IA',
|
|
7536
|
+
'wu.sec.operations': 'Operaciones',
|
|
7537
|
+
'wu.sec.workspace': 'Espacio de trabajo {name}',
|
|
7538
|
+
'wu.sec.instance': 'Instancia',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': 'Requiere atención',
|
|
7519
7541
|
};
|
package/src/builder/i18n/fr.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const fr: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': 'Les récupérer à nouveau après (heures)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': 'De 1 à 168. Les pages sont récupérées à nouveau à la première page ouverte après ce délai, s’il y a une connexion.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': 'Les récupérer à nouveau après : un nombre entier d’heures entre 1 et 168.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'Plus d’actions',
|
|
7521
|
+
'ws.suggest': 'Suggérer',
|
|
7522
|
+
'ws.suggest_title': 'Suggestions au curseur (Ctrl+Espace)',
|
|
7523
|
+
'ws.check': 'Vérifier',
|
|
7524
|
+
'ws.check_title': 'Planifier le SQL sans l’exécuter, comme l’Advisor',
|
|
7525
|
+
'ws.key.indent': 'Indenter (Échap puis Tab : champ suivant)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} en premier dans le chemin de recherche',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'Afficher les libellés',
|
|
7529
|
+
'rail.labels_hide': 'Masquer les libellés',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'Filtrer les utilitaires',
|
|
7532
|
+
'wu.filter_none': 'Rien ne correspond au filtre.',
|
|
7533
|
+
'wu.sec.people': 'Personnes et connexion',
|
|
7534
|
+
'wu.sec.connections': 'Connexions',
|
|
7535
|
+
'wu.sec.data': 'Données et IA',
|
|
7536
|
+
'wu.sec.operations': 'Exploitation',
|
|
7537
|
+
'wu.sec.workspace': 'Espace de travail {name}',
|
|
7538
|
+
'wu.sec.instance': 'Instance',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': 'À traiter',
|
|
7519
7541
|
};
|
package/src/builder/i18n/it.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const it: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': 'Recuperarle di nuovo dopo (ore)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': 'Da 1 a 168. Le pagine vengono recuperate di nuovo alla prima pagina aperta dopo questo tempo, se c’è connessione.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': 'Recuperarle di nuovo dopo: un numero intero di ore da 1 a 168.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'Altre azioni',
|
|
7521
|
+
'ws.suggest': 'Suggerisci',
|
|
7522
|
+
'ws.suggest_title': 'Suggerimenti al cursore (Ctrl+Spazio)',
|
|
7523
|
+
'ws.check': 'Verifica',
|
|
7524
|
+
'ws.check_title': 'Pianifica l’SQL senza eseguirlo, come l’Advisor',
|
|
7525
|
+
'ws.key.indent': 'Rientra (Esc, poi Tab: campo successivo)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} per primo nel percorso di ricerca',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'Mostra etichette',
|
|
7529
|
+
'rail.labels_hide': 'Nascondi etichette',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'Filtra le utilità',
|
|
7532
|
+
'wu.filter_none': 'Nessun risultato per il filtro.',
|
|
7533
|
+
'wu.sec.people': 'Persone e accesso',
|
|
7534
|
+
'wu.sec.connections': 'Connessioni',
|
|
7535
|
+
'wu.sec.data': 'Dati e IA',
|
|
7536
|
+
'wu.sec.operations': 'Operazioni',
|
|
7537
|
+
'wu.sec.workspace': 'Area di lavoro {name}',
|
|
7538
|
+
'wu.sec.instance': 'Istanza',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': 'Richiede attenzione',
|
|
7519
7541
|
};
|
package/src/builder/i18n/ja.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const ja: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': '再取得までの時間(時間)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': '1~168。この時間が過ぎた後、接続があるときに最初にページを開くと再取得されます。',
|
|
7518
7518
|
'pwa.prefetch_hours_range': '再取得までの時間: 1~168 の整数の時間を指定してください。',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'その他の操作',
|
|
7521
|
+
'ws.suggest': '候補',
|
|
7522
|
+
'ws.suggest_title': 'カーソル位置の候補 (Ctrl+Space)',
|
|
7523
|
+
'ws.check': 'チェック',
|
|
7524
|
+
'ws.check_title': 'Advisor と同様に、実行せずに SQL を計画します',
|
|
7525
|
+
'ws.key.indent': 'インデント(Esc の後 Tab: 次のフィールド)',
|
|
7526
|
+
'ws.search_path_plain': '検索パスの先頭は {schema}',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'ラベルを表示',
|
|
7529
|
+
'rail.labels_hide': 'ラベルを非表示',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'ユーティリティを絞り込む',
|
|
7532
|
+
'wu.filter_none': 'フィルターに一致するものはありません。',
|
|
7533
|
+
'wu.sec.people': 'ユーザーとサインイン',
|
|
7534
|
+
'wu.sec.connections': '接続',
|
|
7535
|
+
'wu.sec.data': 'データと AI',
|
|
7536
|
+
'wu.sec.operations': '運用',
|
|
7537
|
+
'wu.sec.workspace': 'ワークスペース {name}',
|
|
7538
|
+
'wu.sec.instance': 'インスタンス',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': '要対応',
|
|
7519
7541
|
};
|
package/src/builder/i18n/ko.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const ko: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': '다시 가져오기까지의 시간(시간)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': '1~168. 이 시간이 지난 뒤 연결되어 있을 때 처음 페이지를 열면 다시 가져옵니다.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': '다시 가져오기까지의 시간: 1~168 사이의 정수 시간입니다.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': '추가 작업',
|
|
7521
|
+
'ws.suggest': '제안',
|
|
7522
|
+
'ws.suggest_title': '커서 위치의 제안 (Ctrl+Space)',
|
|
7523
|
+
'ws.check': '검사',
|
|
7524
|
+
'ws.check_title': 'Advisor처럼 실행하지 않고 SQL을 계획합니다',
|
|
7525
|
+
'ws.key.indent': '들여쓰기 (Esc 후 Tab: 다음 필드)',
|
|
7526
|
+
'ws.search_path_plain': '검색 경로의 첫 번째는 {schema}',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': '레이블 표시',
|
|
7529
|
+
'rail.labels_hide': '레이블 숨기기',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': '유틸리티 필터',
|
|
7532
|
+
'wu.filter_none': '필터와 일치하는 항목이 없습니다.',
|
|
7533
|
+
'wu.sec.people': '사용자 및 로그인',
|
|
7534
|
+
'wu.sec.connections': '연결',
|
|
7535
|
+
'wu.sec.data': '데이터 및 AI',
|
|
7536
|
+
'wu.sec.operations': '운영',
|
|
7537
|
+
'wu.sec.workspace': '작업 공간 {name}',
|
|
7538
|
+
'wu.sec.instance': '인스턴스',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': '주의 필요',
|
|
7519
7541
|
};
|
package/src/builder/i18n/nl.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const nl: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': 'Opnieuw ophalen na (uren)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': '1 tot 168. De pagina\'s worden opnieuw opgehaald bij de eerste pagina die na deze tijd wordt geopend, als er verbinding is.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': 'Opnieuw ophalen na: een geheel aantal uren van 1 tot 168.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'Meer acties',
|
|
7521
|
+
'ws.suggest': 'Suggesties',
|
|
7522
|
+
'ws.suggest_title': 'Suggesties bij de cursor (Ctrl+Spatie)',
|
|
7523
|
+
'ws.check': 'Controleren',
|
|
7524
|
+
'ws.check_title': 'De SQL plannen zonder hem uit te voeren, zoals de Advisor',
|
|
7525
|
+
'ws.key.indent': 'Inspringen (Esc, dan Tab: volgend veld)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} eerst in het zoekpad',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'Labels tonen',
|
|
7529
|
+
'rail.labels_hide': 'Labels verbergen',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'Hulpmiddelen filteren',
|
|
7532
|
+
'wu.filter_none': 'Niets voldoet aan het filter.',
|
|
7533
|
+
'wu.sec.people': 'Mensen en aanmelden',
|
|
7534
|
+
'wu.sec.connections': 'Verbindingen',
|
|
7535
|
+
'wu.sec.data': 'Gegevens en AI',
|
|
7536
|
+
'wu.sec.operations': 'Beheer',
|
|
7537
|
+
'wu.sec.workspace': 'Werkruimte {name}',
|
|
7538
|
+
'wu.sec.instance': 'Instantie',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': 'Vraagt aandacht',
|
|
7519
7541
|
};
|
package/src/builder/i18n/pt.ts
CHANGED
|
@@ -7516,4 +7516,26 @@ export const pt: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': 'Buscá-las novamente após (horas)',
|
|
7517
7517
|
'pwa.prefetch_hours_help': 'De 1 a 168. As páginas são buscadas novamente na primeira página aberta após esse tempo, quando houver conexão.',
|
|
7518
7518
|
'pwa.prefetch_hours_range': 'Buscá-las novamente após: um número inteiro de horas de 1 a 168.',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': 'Mais ações',
|
|
7521
|
+
'ws.suggest': 'Sugerir',
|
|
7522
|
+
'ws.suggest_title': 'Sugestões no cursor (Ctrl+Espaço)',
|
|
7523
|
+
'ws.check': 'Verificar',
|
|
7524
|
+
'ws.check_title': 'Planejar o SQL sem executá-lo, como o Advisor',
|
|
7525
|
+
'ws.key.indent': 'Recuar (Esc e depois Tab: próximo campo)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} primeiro no caminho de pesquisa',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': 'Mostrar rótulos',
|
|
7529
|
+
'rail.labels_hide': 'Ocultar rótulos',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': 'Filtrar os utilitários',
|
|
7532
|
+
'wu.filter_none': 'Nada corresponde ao filtro.',
|
|
7533
|
+
'wu.sec.people': 'Pessoas e login',
|
|
7534
|
+
'wu.sec.connections': 'Conexões',
|
|
7535
|
+
'wu.sec.data': 'Dados e IA',
|
|
7536
|
+
'wu.sec.operations': 'Operações',
|
|
7537
|
+
'wu.sec.workspace': 'Espaço de trabalho {name}',
|
|
7538
|
+
'wu.sec.instance': 'Instância',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': 'Requer atenção',
|
|
7519
7541
|
};
|
|
@@ -7516,4 +7516,26 @@ export const zhCN: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': '多少小时后重新获取',
|
|
7517
7517
|
'pwa.prefetch_hours_help': '1 到 168。超过此时间后,在有网络连接时打开第一个页面时会重新获取这些页面。',
|
|
7518
7518
|
'pwa.prefetch_hours_range': '重新获取时间:1 到 168 之间的整数小时。',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': '更多操作',
|
|
7521
|
+
'ws.suggest': '建议',
|
|
7522
|
+
'ws.suggest_title': '光标处的建议 (Ctrl+Space)',
|
|
7523
|
+
'ws.check': '检查',
|
|
7524
|
+
'ws.check_title': '像 Advisor 一样规划 SQL 而不运行',
|
|
7525
|
+
'ws.key.indent': '缩进(先按 Esc 再按 Tab:下一个字段)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} 位于搜索路径首位',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': '显示标签',
|
|
7529
|
+
'rail.labels_hide': '隐藏标签',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': '筛选实用工具',
|
|
7532
|
+
'wu.filter_none': '没有符合筛选条件的项目。',
|
|
7533
|
+
'wu.sec.people': '人员与登录',
|
|
7534
|
+
'wu.sec.connections': '连接',
|
|
7535
|
+
'wu.sec.data': '数据与 AI',
|
|
7536
|
+
'wu.sec.operations': '运维',
|
|
7537
|
+
'wu.sec.workspace': '工作区 {name}',
|
|
7538
|
+
'wu.sec.instance': '实例',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': '需要注意',
|
|
7519
7541
|
};
|
|
@@ -7516,4 +7516,26 @@ export const zhTW: BuilderMessages = {
|
|
|
7516
7516
|
'pwa.prefetch_hours': '多少小時後重新取得',
|
|
7517
7517
|
'pwa.prefetch_hours_help': '1 到 168。超過此時間後,在有網路連線時開啟第一個頁面時會重新取得這些頁面。',
|
|
7518
7518
|
'pwa.prefetch_hours_range': '重新取得時間:1 到 168 之間的整數小時。',
|
|
7519
|
+
// ------------------------------------------------------------ SQL Worksheet: one toolbar row (UX-1)
|
|
7520
|
+
'ws.more': '更多動作',
|
|
7521
|
+
'ws.suggest': '建議',
|
|
7522
|
+
'ws.suggest_title': '游標處的建議 (Ctrl+Space)',
|
|
7523
|
+
'ws.check': '檢查',
|
|
7524
|
+
'ws.check_title': '像 Advisor 一樣規劃 SQL 而不執行',
|
|
7525
|
+
'ws.key.indent': '縮排(先按 Esc 再按 Tab:下一個欄位)',
|
|
7526
|
+
'ws.search_path_plain': '{schema} 位於搜尋路徑首位',
|
|
7527
|
+
// ------------------------------------------------------------ Rail labels (UX-3)
|
|
7528
|
+
'rail.labels_show': '顯示標籤',
|
|
7529
|
+
'rail.labels_hide': '隱藏標籤',
|
|
7530
|
+
// ------------------------------------------------------------ Administration page (UX-4)
|
|
7531
|
+
'wu.filter': '篩選公用程式',
|
|
7532
|
+
'wu.filter_none': '沒有符合篩選條件的項目。',
|
|
7533
|
+
'wu.sec.people': '人員與登入',
|
|
7534
|
+
'wu.sec.connections': '連線',
|
|
7535
|
+
'wu.sec.data': '資料與 AI',
|
|
7536
|
+
'wu.sec.operations': '維運',
|
|
7537
|
+
'wu.sec.workspace': '工作區 {name}',
|
|
7538
|
+
'wu.sec.instance': '執行個體',
|
|
7539
|
+
// ------------------------------------------------------------ Workspace home: needs attention (UX-13)
|
|
7540
|
+
'wh.attention': '需要注意',
|
|
7519
7541
|
};
|
package/src/builder/routes.ts
CHANGED
|
@@ -540,6 +540,24 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
540
540
|
return reply.redirect(`${BASE}/login`, 303);
|
|
541
541
|
});
|
|
542
542
|
|
|
543
|
+
// (UX-3) the icon rail with labels next to the icons, or icons only (kept in the developer's session)
|
|
544
|
+
app.post(`${BASE}/rail`, async (req: Req, reply) => {
|
|
545
|
+
const s = await developer(req, reply);
|
|
546
|
+
if (!s) return;
|
|
547
|
+
if (req.body?.labels === 'labels') s.state.__RAIL = 'labels';
|
|
548
|
+
else delete s.state.__RAIL;
|
|
549
|
+
const ref = String(req.headers.referer ?? '');
|
|
550
|
+
let to = `${BASE}/home`;
|
|
551
|
+
try {
|
|
552
|
+
const u = new URL(ref);
|
|
553
|
+
if (u.host === req.headers.host && u.pathname.startsWith(BASE)) to = u.pathname + u.search;
|
|
554
|
+
} catch {
|
|
555
|
+
/* no referer: the home page */
|
|
556
|
+
}
|
|
557
|
+
await saveState(s);
|
|
558
|
+
return reply.redirect(to, 303);
|
|
559
|
+
});
|
|
560
|
+
|
|
543
561
|
// The builder's light/dark theme (the developer's menu in the icon rail).
|
|
544
562
|
app.post(`${BASE}/theme`, async (req: Req, reply) => {
|
|
545
563
|
const s = await developer(req, reply);
|
|
@@ -654,7 +672,7 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
654
672
|
const tx = tr(s);
|
|
655
673
|
|
|
656
674
|
const main = html`
|
|
657
|
-
${appHeader(a, 'pages', s)}
|
|
675
|
+
${appHeader(a, 'pages', s, [`${BASE}/apps/${a.id}/create-page`, tx('app.create_page'), 'plus'])}
|
|
658
676
|
${appLock && appLock.locked_by !== s.username ? html`<div class="alert alert-error" role="status">${icon('key')} ${lockText(appLock)} ${tx('app.lock_refused')}</div>` : ''}
|
|
659
677
|
<div class="app-home">
|
|
660
678
|
<div class="app-home-main">
|
|
@@ -664,7 +682,6 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
664
682
|
<button class="btn">${icon('search')}<span class="sr-only">${tx('common.search')}</span></button></form>
|
|
665
683
|
<div class="seg view-toggle" role="group" aria-label="${tx('ab.view')}">
|
|
666
684
|
<a href="${keep({})}"${icons ? '' : raw(' aria-current="true"')}>${icon('list')} ${tx('ab.view_report')}</a><a href="${keep({ view: 'icons' })}"${icons ? raw(' aria-current="true"') : ''}>${icon('grid')} ${tx('app.view_icons')}</a></div>
|
|
667
|
-
<a class="btn btn-hot" href="${BASE}/apps/${a.id}/create-page">${icon('plus')} ${tx('app.create_page')}</a>
|
|
668
685
|
</div>
|
|
669
686
|
${region(tx('app.tab.pages'), html`${grouped
|
|
670
687
|
? html`<form method="get" class="search u-mwnone u-mb1"><div class="field"><label class="label" for="f_group">${tx('app.page_group')}</label>
|
|
@@ -690,7 +707,7 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
690
707
|
${select('kind', tx('app.page_type'), 'report_form', WIZARD_KINDS.map(([k, label]): [string, string] => [k, kindLabel(k, label)]))}
|
|
691
708
|
${select('table', tx('app.table_or_view'), '', tables.map((t): [string, string] => [t.t, t.access ? t.t : tx('app.no_access', { table: t.t, role: a.db_role })]))}
|
|
692
709
|
</div>
|
|
693
|
-
<div class="buttons"><button class="btn
|
|
710
|
+
<div class="buttons"><button class="btn">${tx('common.next')}</button></div>
|
|
694
711
|
</form>
|
|
695
712
|
<p><a href="${BASE}/apps/${a.id}/create-page">${tx('app.all_page_types')} ▸</a></p>
|
|
696
713
|
<p><a href="${BASE}/apps/${a.id}/ai-pages">${tx('app.ai_pages')} ▸</a> <span class="muted">${tx('app.ai_pages_note')}</span></p>`)}
|
|
@@ -702,25 +719,19 @@ export async function builderRoutes(app: FastifyInstance) {
|
|
|
702
719
|
${select('mode', tx('app.col.mode'), 'normal', ['normal', 'modal'])}
|
|
703
720
|
${input('parent_page', tx('app.parent'), a.home_page, { type: 'number' })}
|
|
704
721
|
</div>
|
|
705
|
-
<div class="buttons"><button class="btn
|
|
722
|
+
<div class="buttons"><button class="btn">${tx('app.create_page')}</button></div>
|
|
706
723
|
</form>`)}
|
|
707
724
|
</div>
|
|
708
725
|
</div>
|
|
709
726
|
<aside class="app-tasks region region-standard" aria-labelledby="app-tasks-h"><header class="region-header"><h2 id="app-tasks-h">${tx('common.tasks')}</h2></header>
|
|
710
727
|
<ul class="sc-links">
|
|
711
|
-
<li><a href="/a/${a.alias}" target="_blank" rel="noopener">${icon('play')} ${tx('app.task.run')}</a></li>
|
|
712
|
-
<li><a href="${BASE}/apps/${a.id}/create-page">${icon('plus')} ${tx('app.create_page')}</a></li>
|
|
713
728
|
<li><a href="${BASE}/apps/${a.id}/create-page?tab=feature">${icon('puzzle')} ${tx('app.task.add_feature')}</a></li>
|
|
714
729
|
<li><a href="${BASE}/apps/${a.id}/create-page?tab=copy">${icon('copy')} ${tx('app.task.copy_page')}</a></li>
|
|
715
730
|
<li><a href="${BASE}/apps/${a.id}/ai-pages">${icon('lightbulb')} ${tx('app.ai_pages')}</a></li>
|
|
716
|
-
<li><a href="${BASE}/apps/${a.id}/shared">${icon('layers')} ${tx('app.tab.shared')}</a></li>
|
|
717
731
|
<li><a href="${BASE}/apps/${a.id}/shared?type=page_group">${icon('folder')} ${tx('app.task.page_groups')}</a></li>
|
|
718
|
-
<li><a href="${BASE}/apps/${a.id}/export">${icon('download')} ${tx('app.head.export')}</a></li>
|
|
719
|
-
<li><a href="${BASE}/apps/${a.id}/backups">${icon('history')} ${tx('app.head.backups')}</a></li>
|
|
720
732
|
<li><a href="${BASE}/apps/${a.id}/supporting-objects">${icon('database')} ${tx('app.task.supporting')}</a></li>
|
|
721
733
|
<li><a href="#app-lock">${icon('lock')} ${tx('app.task.lock')}</a></li>
|
|
722
734
|
${issues.task}
|
|
723
|
-
<li><a href="${BASE}/apps/${a.id}/settings?tab=definition">${icon('trash')} ${tx('app.task.delete')}</a></li>
|
|
724
735
|
</ul></aside>
|
|
725
736
|
</div>`;
|
|
726
737
|
return send(reply, s, shell(s, a.name, [[tx('rte.app_builder'), BASE], [a.name]], main));
|
package/src/builder/sql.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { FastifyInstance, FastifyReply } from 'fastify';
|
|
|
2
2
|
import pg from 'pg';
|
|
3
3
|
import { html, raw, type Raw } from '../html.ts';
|
|
4
4
|
import { icon } from '../icons.ts';
|
|
5
|
-
import { back, BASE, csrf, developer, flash, region, send, shell, workshopTabs, type Req } from './ui.ts';
|
|
5
|
+
import { back, BASE, csrf, developer, flash, pageHeader, region, send, shell, workshopTabs, type Req } from './ui.ts';
|
|
6
6
|
import { owner } from '../db.ts';
|
|
7
7
|
import { clientIp, logActivity, type Session } from '../session.ts';
|
|
8
8
|
import { clearCompletions } from './code-editor.ts';
|
|
@@ -70,7 +70,7 @@ async function workshopHome(req: Req, reply: FastifyReply) {
|
|
|
70
70
|
html`<section class="sw-recent"><h2>${title}</h2>${rows.length ? html`<ul>${rows}</ul>` : html`<p class="muted">${empty}</p>`}</section>`;
|
|
71
71
|
const main = html`<div class="ab-home">
|
|
72
72
|
<div class="ab-main">
|
|
73
|
-
|
|
73
|
+
${pageHeader(tx('rail.sql_workshop'))}
|
|
74
74
|
${workshopTabs('home', s)}
|
|
75
75
|
${await launchpad(s, await isAdmin(s.username))}
|
|
76
76
|
<div class="sw-recents">
|