pgkiln 0.35.0 → 0.36.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/db/migrations/087_login_page.sql +175 -0
- package/db/migrations/088_region_templates.sql +18 -0
- package/db/migrations/089_page_groups.sql +330 -0
- package/docs/apex-feature-parity.md +22 -20
- package/docs/guide/03-builder.md +36 -2
- package/docs/guide/04-pages-and-regions.md +42 -3
- package/docs/guide/08-security.md +46 -5
- package/docs/guide/11-from-apex.md +4 -1
- package/docs/guide/12-development.md +7 -1
- package/docs/guide/17-mobile.md +3 -1
- package/docs/guide/18-cli.md +2 -0
- package/docs/guide/20-ai-agents.md +2 -0
- package/examples/hr/hr_54_region_templates.sql +67 -0
- package/examples/hr/hr_55_page_groups.sql +24 -0
- package/package.json +1 -1
- package/public/app.css +60 -3
- package/public/app.js +89 -3
- package/public/builder.css +11 -0
- package/public/builder.js +50 -0
- package/public/code-editor.css +3 -3
- package/public/sw.js +5 -3
- package/src/app.ts +2 -0
- package/src/appfiles.ts +1 -0
- package/src/builder/advisor.ts +14 -0
- package/src/builder/components.ts +15 -2
- package/src/builder/ddl-tools.ts +114 -0
- package/src/builder/designer.ts +29 -3
- package/src/builder/forms.ts +6 -4
- package/src/builder/login-page.ts +82 -0
- package/src/builder/region-help.ts +4 -2
- package/src/builder/region-settings.ts +19 -3
- package/src/builder/routes.ts +16 -4
- package/src/builder/shared.ts +1 -1
- package/src/builder/sql.ts +3 -0
- package/src/builder/ui.ts +2 -2
- package/src/cli/main.ts +45 -0
- package/src/cli/mcp.ts +42 -0
- package/src/cli/replace.ts +4 -0
- package/src/ddl.ts +509 -0
- package/src/i18n/ar.ts +17 -0
- package/src/i18n/cs.ts +17 -0
- package/src/i18n/da.ts +17 -0
- package/src/i18n/de.ts +17 -0
- package/src/i18n/el.ts +17 -0
- package/src/i18n/es.ts +17 -0
- package/src/i18n/fi.ts +17 -0
- package/src/i18n/fr.ts +17 -0
- package/src/i18n/he.ts +17 -0
- package/src/i18n/it.ts +17 -0
- package/src/i18n/ja.ts +17 -0
- package/src/i18n/ko.ts +17 -0
- package/src/i18n/nb.ts +17 -0
- package/src/i18n/pl.ts +17 -0
- package/src/i18n/pt.ts +17 -0
- package/src/i18n/ru.ts +17 -0
- package/src/i18n/sv.ts +17 -0
- package/src/i18n/tr.ts +17 -0
- package/src/i18n/uk.ts +17 -0
- package/src/i18n/zh.ts +17 -0
- package/src/i18n.ts +34 -0
- package/src/icons.ts +2 -1
- package/src/layout.ts +3 -2
- package/src/metadata.ts +10 -4
- package/src/runtime/devbar.ts +3 -1
- package/src/runtime/display-selector.ts +12 -4
- package/src/runtime/grid.ts +170 -26
- package/src/runtime/lists.ts +11 -1
- package/src/runtime/password-reset.ts +171 -0
- package/src/runtime/pwa.ts +7 -10
- package/src/runtime/regions.ts +33 -0
- package/src/runtime/render.ts +15 -0
- package/src/runtime/report-views.ts +4 -2
- package/src/runtime/report.ts +36 -22
- package/src/runtime/routes.ts +35 -32
- package/src/runtime/signin.ts +80 -0
- package/src/static-version.ts +34 -0
package/src/i18n/es.ts
CHANGED
|
@@ -562,6 +562,23 @@ export const es: Record<MessageKey, string> = {
|
|
|
562
562
|
'grid.column_position': 'Posición de {column}',
|
|
563
563
|
'grid.column_width': 'Ancho de {column} en píxeles',
|
|
564
564
|
'grid.default_aggregates': 'Siempre visible: {list}.',
|
|
565
|
+
'reset.link': "¿Has olvidado la contraseña?",
|
|
566
|
+
'reset.title': "Restablecer la contraseña",
|
|
567
|
+
'reset.intro': "Escribe tu nombre de usuario o tu correo electrónico. Si pertenece a una cuenta, te enviaremos un enlace para elegir una contraseña nueva.",
|
|
568
|
+
'reset.login_label': "Usuario o correo electrónico",
|
|
569
|
+
'reset.send': "Enviar enlace",
|
|
570
|
+
'reset.sent': "Si coincide con una cuenta, va de camino un correo con un enlace. El enlace funciona una vez, durante 30 minutos.",
|
|
571
|
+
'reset.mail_subject': "Elige una contraseña nueva para {app}",
|
|
572
|
+
'reset.mail_body': "Hola, #USER#:\n\nAlguien, esperemos que tú, ha pedido elegir una contraseña nueva para {app}. Abre este enlace en los próximos 30 minutos para hacerlo:\n\n{link}\n\nSi no lo has pedido, puedes ignorar este correo: tu contraseña sigue igual.",
|
|
573
|
+
'reset.new_title': "Elige una contraseña nueva",
|
|
574
|
+
'reset.invalid': "Este enlace no es válido (o ya no lo es). Pide uno nuevo.",
|
|
575
|
+
'reset.done': "Tu contraseña se ha cambiado. Inicia sesión con la nueva.",
|
|
576
|
+
'reset.back': "Volver a iniciar sesión",
|
|
577
|
+
'grid.view_grid': 'Cuadrícula',
|
|
578
|
+
'grid.view_row': 'Vista de una fila',
|
|
579
|
+
'grid.row_of': 'Fila {row} de {total}',
|
|
580
|
+
'grid.first': 'Primera',
|
|
581
|
+
'grid.last': 'Última',
|
|
565
582
|
'logic.branch_bad_url': 'La bifurcación "{name}" devolvió una URL que no es una ruta dentro de la aplicación.',
|
|
566
583
|
'logic.branch_no_app': 'La bifurcación "{name}" lleva a la página {page} de la aplicación {app}, que no existe.',
|
|
567
584
|
'download.none': 'No hay ningún archivo para descargar.',
|
package/src/i18n/fi.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const fi: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Sarakkeen {column} paikka",
|
|
449
449
|
'grid.column_width': "Sarakkeen {column} leveys pikseleinä",
|
|
450
450
|
'grid.default_aggregates': "Näytetään aina: {list}.",
|
|
451
|
+
'reset.link': "Unohtuiko salasana?",
|
|
452
|
+
'reset.title': "Salasanan palautus",
|
|
453
|
+
'reset.intro': "Kirjoita käyttäjätunnuksesi tai sähköpostiosoitteesi. Jos se kuuluu tilille, lähetämme linkin uuden salasanan valitsemiseen.",
|
|
454
|
+
'reset.login_label': "Käyttäjätunnus tai sähköposti",
|
|
455
|
+
'reset.send': "Lähetä linkki",
|
|
456
|
+
'reset.sent': "Jos tiedot vastaavat tiliä, sähköposti linkin kanssa on matkalla. Linkki toimii kerran, 30 minuutin ajan.",
|
|
457
|
+
'reset.mail_subject': "Valitse uusi salasana sovellukseen {app}",
|
|
458
|
+
'reset.mail_body': "Hei #USER#,\n\njoku, toivottavasti sinä, pyysi uuden salasanan valitsemista sovellukseen {app}. Avaa tämä linkki 30 minuutin kuluessa:\n\n{link}\n\nJos et pyytänyt tätä, voit jättää viestin huomiotta: salasanasi pysyy ennallaan.",
|
|
459
|
+
'reset.new_title': "Valitse uusi salasana",
|
|
460
|
+
'reset.invalid': "Linkki ei ole (enää) voimassa. Pyydä uusi.",
|
|
461
|
+
'reset.done': "Salasanasi on vaihdettu. Kirjaudu sisään uudella salasanalla.",
|
|
462
|
+
'reset.back': "Takaisin kirjautumiseen",
|
|
463
|
+
'grid.view_grid': "Ruudukko",
|
|
464
|
+
'grid.view_row': "Yhden rivin näkymä",
|
|
465
|
+
'grid.row_of': "Rivi {row}/{total}",
|
|
466
|
+
'grid.first': "Ensimmäinen",
|
|
467
|
+
'grid.last': "Viimeinen",
|
|
451
468
|
'chart.other': "Muut",
|
|
452
469
|
'chart.label': "Otsikko",
|
|
453
470
|
'chart.bubble': "Kupla",
|
package/src/i18n/fr.ts
CHANGED
|
@@ -562,6 +562,23 @@ export const fr: Record<MessageKey, string> = {
|
|
|
562
562
|
'grid.column_position': 'Position de {column}',
|
|
563
563
|
'grid.column_width': 'Largeur de {column} en pixels',
|
|
564
564
|
'grid.default_aggregates': 'Toujours affiché : {list}.',
|
|
565
|
+
'reset.link': "Mot de passe oublié ?",
|
|
566
|
+
'reset.title': "Réinitialiser votre mot de passe",
|
|
567
|
+
'reset.intro': "Saisissez votre nom d'utilisateur ou votre adresse e-mail. S'il correspond à un compte, nous vous enverrons un lien pour choisir un nouveau mot de passe.",
|
|
568
|
+
'reset.login_label': "Nom d'utilisateur ou e-mail",
|
|
569
|
+
'reset.send': "Envoyer le lien",
|
|
570
|
+
'reset.sent': "Si cela correspond à un compte, un e-mail avec un lien est en route. Le lien fonctionne une fois, pendant 30 minutes.",
|
|
571
|
+
'reset.mail_subject': "Choisissez un nouveau mot de passe pour {app}",
|
|
572
|
+
'reset.mail_body': "Bonjour #USER#,\n\nQuelqu'un, vous sans doute, a demandé à choisir un nouveau mot de passe pour {app}. Ouvrez ce lien dans les 30 minutes pour le faire :\n\n{link}\n\nSi vous n'avez rien demandé, ignorez cet e-mail : votre mot de passe reste inchangé.",
|
|
573
|
+
'reset.new_title': "Choisir un nouveau mot de passe",
|
|
574
|
+
'reset.invalid': "Ce lien n'est pas (ou plus) valide. Demandez-en un nouveau.",
|
|
575
|
+
'reset.done': "Votre mot de passe a été modifié. Connectez-vous avec le nouveau.",
|
|
576
|
+
'reset.back': "Retour à la connexion",
|
|
577
|
+
'grid.view_grid': 'Grille',
|
|
578
|
+
'grid.view_row': 'Vue ligne unique',
|
|
579
|
+
'grid.row_of': 'Ligne {row} sur {total}',
|
|
580
|
+
'grid.first': 'Première',
|
|
581
|
+
'grid.last': 'Dernière',
|
|
565
582
|
'logic.branch_bad_url': 'Le branchement « {name} » a renvoyé une URL qui n’est pas un chemin dans l’application.',
|
|
566
583
|
'logic.branch_no_app': 'Le branchement « {name} » mène à la page {page} de l’application {app}, qui n’existe pas.',
|
|
567
584
|
'download.none': 'Il n’y a aucun fichier à télécharger.',
|
package/src/i18n/he.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const he: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "מיקום {column}",
|
|
449
449
|
'grid.column_width': "רוחב {column} בפיקסלים",
|
|
450
450
|
'grid.default_aggregates': "מוצגים תמיד: {list}.",
|
|
451
|
+
'reset.link': "שכחת את הסיסמה?",
|
|
452
|
+
'reset.title': "איפוס סיסמה",
|
|
453
|
+
'reset.intro': "הזן את שם המשתמש או את כתובת הדוא\"ל שלך. אם היא שייכת לחשבון, נשלח לך קישור לבחירת סיסמה חדשה.",
|
|
454
|
+
'reset.login_label': "שם משתמש או דוא\"ל",
|
|
455
|
+
'reset.send': "שליחת קישור",
|
|
456
|
+
'reset.sent': "אם זה תואם לחשבון, הודעת דוא\"ל עם קישור בדרך. הקישור פועל פעם אחת, למשך 30 דקות.",
|
|
457
|
+
'reset.mail_subject': "בחירת סיסמה חדשה ל-{app}",
|
|
458
|
+
'reset.mail_body': "שלום #USER#,\n\nמישהו, אנחנו מקווים שאתה, ביקש לבחור סיסמה חדשה ל-{app}. פתח את הקישור הזה תוך 30 דקות כדי לעשות זאת:\n\n{link}\n\nאם לא ביקשת זאת, אפשר להתעלם מהודעה זו: הסיסמה שלך תישאר כפי שהיא.",
|
|
459
|
+
'reset.new_title': "בחירת סיסמה חדשה",
|
|
460
|
+
'reset.invalid': "הקישור הזה אינו תקף (עוד). בקש קישור חדש.",
|
|
461
|
+
'reset.done': "הסיסמה שלך שונתה. התחבר עם הסיסמה החדשה.",
|
|
462
|
+
'reset.back': "חזרה להתחברות",
|
|
463
|
+
'grid.view_grid': "רשת",
|
|
464
|
+
'grid.view_row': "תצוגת שורה בודדת",
|
|
465
|
+
'grid.row_of': "שורה {row} מתוך {total}",
|
|
466
|
+
'grid.first': "ראשונה",
|
|
467
|
+
'grid.last': "אחרונה",
|
|
451
468
|
'chart.other': "אחר",
|
|
452
469
|
'chart.label': "תווית",
|
|
453
470
|
'chart.bubble': "בועות",
|
package/src/i18n/it.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const it: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Posizione di {column}",
|
|
449
449
|
'grid.column_width': "Larghezza di {column} in pixel",
|
|
450
450
|
'grid.default_aggregates': "Sempre mostrati: {list}.",
|
|
451
|
+
'reset.link': "Password dimenticata?",
|
|
452
|
+
'reset.title': "Reimposta la password",
|
|
453
|
+
'reset.intro': "Inserisci il tuo nome utente o indirizzo e-mail. Se appartiene a un account, ti invieremo un link per scegliere una nuova password.",
|
|
454
|
+
'reset.login_label': "Nome utente o e-mail",
|
|
455
|
+
'reset.send': "Invia link",
|
|
456
|
+
'reset.sent': "Se corrisponde a un account, è in arrivo un'e-mail con un link. Il link funziona una volta, per 30 minuti.",
|
|
457
|
+
'reset.mail_subject': "Scegli una nuova password per {app}",
|
|
458
|
+
'reset.mail_body': "Ciao #USER#,\n\nqualcuno, si spera tu, ha chiesto di scegliere una nuova password per {app}. Apri questo link entro 30 minuti per farlo:\n\n{link}\n\nSe non l'hai chiesto tu, ignora questa e-mail: la tua password resta invariata.",
|
|
459
|
+
'reset.new_title': "Scegli una nuova password",
|
|
460
|
+
'reset.invalid': "Questo link non è (più) valido. Richiedine uno nuovo.",
|
|
461
|
+
'reset.done': "La password è stata cambiata. Accedi con quella nuova.",
|
|
462
|
+
'reset.back': "Torna all'accesso",
|
|
463
|
+
'grid.view_grid': "Griglia",
|
|
464
|
+
'grid.view_row': "Vista riga singola",
|
|
465
|
+
'grid.row_of': "Riga {row} di {total}",
|
|
466
|
+
'grid.first': "Prima",
|
|
467
|
+
'grid.last': "Ultima",
|
|
451
468
|
'chart.other': "Altro",
|
|
452
469
|
'chart.label': "Etichetta",
|
|
453
470
|
'chart.bubble': "Bolle",
|
package/src/i18n/ja.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const ja: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "{column} の位置",
|
|
449
449
|
'grid.column_width': "{column} の幅 (ピクセル)",
|
|
450
450
|
'grid.default_aggregates': "常に表示: {list}。",
|
|
451
|
+
'reset.link': "パスワードをお忘れですか?",
|
|
452
|
+
'reset.title': "パスワードの再設定",
|
|
453
|
+
'reset.intro': "ユーザー名またはメールアドレスを入力してください。アカウントに該当する場合、新しいパスワードを設定するリンクをお送りします。",
|
|
454
|
+
'reset.login_label': "ユーザー名またはメールアドレス",
|
|
455
|
+
'reset.send': "リンクを送信",
|
|
456
|
+
'reset.sent': "該当するアカウントがあれば、リンク付きのメールをお送りしました。リンクは1回限り、30分間有効です。",
|
|
457
|
+
'reset.mail_subject': "{app} の新しいパスワードを設定してください",
|
|
458
|
+
'reset.mail_body': "#USER# 様\n\n{app} の新しいパスワードの設定が依頼されました。30分以内に次のリンクを開いて設定してください。\n\n{link}\n\n心当たりがない場合は、このメールを無視してください。パスワードは変更されません。",
|
|
459
|
+
'reset.new_title': "新しいパスワードを設定",
|
|
460
|
+
'reset.invalid': "このリンクは無効です(または期限切れです)。もう一度依頼してください。",
|
|
461
|
+
'reset.done': "パスワードを変更しました。新しいパスワードでサインインしてください。",
|
|
462
|
+
'reset.back': "サインインに戻る",
|
|
463
|
+
'grid.view_grid': "グリッド",
|
|
464
|
+
'grid.view_row': "単一行ビュー",
|
|
465
|
+
'grid.row_of': "{total} 行中 {row} 行目",
|
|
466
|
+
'grid.first': "最初",
|
|
467
|
+
'grid.last': "最後",
|
|
451
468
|
'chart.other': "その他",
|
|
452
469
|
'chart.label': "ラベル",
|
|
453
470
|
'chart.bubble': "バブル",
|
package/src/i18n/ko.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const ko: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "{column} 위치",
|
|
449
449
|
'grid.column_width': "{column} 너비(픽셀)",
|
|
450
450
|
'grid.default_aggregates': "항상 표시: {list}.",
|
|
451
|
+
'reset.link': "비밀번호를 잊으셨나요?",
|
|
452
|
+
'reset.title': "비밀번호 재설정",
|
|
453
|
+
'reset.intro': "사용자 이름이나 이메일 주소를 입력하세요. 계정에 해당하면 새 비밀번호를 설정할 수 있는 링크를 보내 드립니다.",
|
|
454
|
+
'reset.login_label': "사용자 이름 또는 이메일",
|
|
455
|
+
'reset.send': "링크 보내기",
|
|
456
|
+
'reset.sent': "일치하는 계정이 있으면 링크가 담긴 이메일을 보냈습니다. 링크는 한 번, 30분 동안 사용할 수 있습니다.",
|
|
457
|
+
'reset.mail_subject': "{app}의 새 비밀번호를 설정하세요",
|
|
458
|
+
'reset.mail_body': "#USER# 님, 안녕하세요.\n\n{app}의 새 비밀번호 설정이 요청되었습니다. 30분 안에 다음 링크를 열어 설정하세요.\n\n{link}\n\n요청하지 않으셨다면 이 이메일을 무시하세요. 비밀번호는 그대로 유지됩니다.",
|
|
459
|
+
'reset.new_title': "새 비밀번호 설정",
|
|
460
|
+
'reset.invalid': "이 링크는 유효하지 않거나 만료되었습니다. 새 링크를 요청하세요.",
|
|
461
|
+
'reset.done': "비밀번호가 변경되었습니다. 새 비밀번호로 로그인하세요.",
|
|
462
|
+
'reset.back': "로그인으로 돌아가기",
|
|
463
|
+
'grid.view_grid': "그리드",
|
|
464
|
+
'grid.view_row': "단일 행 보기",
|
|
465
|
+
'grid.row_of': "{total}개 중 {row}번째 행",
|
|
466
|
+
'grid.first': "처음",
|
|
467
|
+
'grid.last': "마지막",
|
|
451
468
|
'chart.other': "기타",
|
|
452
469
|
'chart.label': "레이블",
|
|
453
470
|
'chart.bubble': "거품형",
|
package/src/i18n/nb.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const nb: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Plassering av {column}",
|
|
449
449
|
'grid.column_width': "Bredde på {column} i piksler",
|
|
450
450
|
'grid.default_aggregates': "Vises alltid: {list}.",
|
|
451
|
+
'reset.link': "Glemt passordet?",
|
|
452
|
+
'reset.title': "Tilbakestill passordet",
|
|
453
|
+
'reset.intro': "Skriv inn brukernavnet eller e-postadressen din. Hvis den hører til en konto, sender vi en lenke der du kan velge et nytt passord.",
|
|
454
|
+
'reset.login_label': "Brukernavn eller e-post",
|
|
455
|
+
'reset.send': "Send lenke",
|
|
456
|
+
'reset.sent': "Hvis det samsvarer med en konto, er en e-post med en lenke på vei. Lenken virker én gang, i 30 minutter.",
|
|
457
|
+
'reset.mail_subject': "Velg et nytt passord for {app}",
|
|
458
|
+
'reset.mail_body': "Hei #USER#,\n\nNoen, forhåpentligvis du, har bedt om å velge et nytt passord for {app}. Åpne denne lenken innen 30 minutter for å gjøre det:\n\n{link}\n\nHvis du ikke ba om dette, kan du se bort fra e-posten: passordet ditt forblir som det er.",
|
|
459
|
+
'reset.new_title': "Velg et nytt passord",
|
|
460
|
+
'reset.invalid': "Denne lenken er ikke (lenger) gyldig. Be om en ny.",
|
|
461
|
+
'reset.done': "Passordet ditt er endret. Logg inn med det nye.",
|
|
462
|
+
'reset.back': "Tilbake til innlogging",
|
|
463
|
+
'grid.view_grid': "Rutenett",
|
|
464
|
+
'grid.view_row': "Enkeltradvisning",
|
|
465
|
+
'grid.row_of': "Rad {row} av {total}",
|
|
466
|
+
'grid.first': "Første",
|
|
467
|
+
'grid.last': "Siste",
|
|
451
468
|
'chart.other': "Annet",
|
|
452
469
|
'chart.label': "Etikett",
|
|
453
470
|
'chart.bubble': "Boble",
|
package/src/i18n/pl.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const pl: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Pozycja kolumny {column}",
|
|
449
449
|
'grid.column_width': "Szerokość kolumny {column} w pikselach",
|
|
450
450
|
'grid.default_aggregates': "Zawsze pokazywane: {list}.",
|
|
451
|
+
'reset.link': "Nie pamiętasz hasła?",
|
|
452
|
+
'reset.title': "Zresetuj hasło",
|
|
453
|
+
'reset.intro': "Wpisz nazwę użytkownika lub adres e-mail. Jeśli należy do konta, wyślemy link do ustawienia nowego hasła.",
|
|
454
|
+
'reset.login_label': "Nazwa użytkownika lub e-mail",
|
|
455
|
+
'reset.send': "Wyślij link",
|
|
456
|
+
'reset.sent': "Jeśli pasuje to do konta, wysłaliśmy e-mail z linkiem. Link działa jeden raz, przez 30 minut.",
|
|
457
|
+
'reset.mail_subject': "Ustaw nowe hasło do {app}",
|
|
458
|
+
'reset.mail_body': "Witaj #USER#,\n\nktoś, miejmy nadzieję, że Ty, poprosił o ustawienie nowego hasła do {app}. Otwórz ten link w ciągu 30 minut:\n\n{link}\n\nJeśli to nie Ty, zignoruj tę wiadomość: Twoje hasło pozostaje bez zmian.",
|
|
459
|
+
'reset.new_title': "Ustaw nowe hasło",
|
|
460
|
+
'reset.invalid': "Ten link jest nieważny. Poproś o nowy.",
|
|
461
|
+
'reset.done': "Hasło zostało zmienione. Zaloguj się nowym hasłem.",
|
|
462
|
+
'reset.back': "Powrót do logowania",
|
|
463
|
+
'grid.view_grid': "Siatka",
|
|
464
|
+
'grid.view_row': "Widok jednego wiersza",
|
|
465
|
+
'grid.row_of': "Wiersz {row} z {total}",
|
|
466
|
+
'grid.first': "Pierwszy",
|
|
467
|
+
'grid.last': "Ostatni",
|
|
451
468
|
'chart.other': "Inne",
|
|
452
469
|
'chart.label': "Etykieta",
|
|
453
470
|
'chart.bubble': "Bąbelkowy",
|
package/src/i18n/pt.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const pt: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Posição de {column}",
|
|
449
449
|
'grid.column_width': "Largura de {column} em píxeis",
|
|
450
450
|
'grid.default_aggregates': "Sempre mostrados: {list}.",
|
|
451
|
+
'reset.link': "Esqueceu a palavra-passe?",
|
|
452
|
+
'reset.title': "Repor a palavra-passe",
|
|
453
|
+
'reset.intro': "Introduza o seu nome de utilizador ou endereço de e-mail. Se pertencer a uma conta, enviamos-lhe uma ligação para escolher uma nova palavra-passe.",
|
|
454
|
+
'reset.login_label': "Nome de utilizador ou e-mail",
|
|
455
|
+
'reset.send': "Enviar ligação",
|
|
456
|
+
'reset.sent': "Se corresponder a uma conta, está a caminho um e-mail com uma ligação. A ligação funciona uma vez, durante 30 minutos.",
|
|
457
|
+
'reset.mail_subject': "Escolha uma nova palavra-passe para {app}",
|
|
458
|
+
'reset.mail_body': "Olá #USER#,\n\nAlguém, esperamos que tenha sido você, pediu para escolher uma nova palavra-passe para {app}. Abra esta ligação dentro de 30 minutos para o fazer:\n\n{link}\n\nSe não fez este pedido, ignore este e-mail: a sua palavra-passe mantém-se.",
|
|
459
|
+
'reset.new_title': "Escolher uma nova palavra-passe",
|
|
460
|
+
'reset.invalid': "Esta ligação não é (ou já não é) válida. Peça uma nova.",
|
|
461
|
+
'reset.done': "A sua palavra-passe foi alterada. Inicie sessão com a nova.",
|
|
462
|
+
'reset.back': "Voltar ao início de sessão",
|
|
463
|
+
'grid.view_grid': "Grelha",
|
|
464
|
+
'grid.view_row': "Vista de linha única",
|
|
465
|
+
'grid.row_of': "Linha {row} de {total}",
|
|
466
|
+
'grid.first': "Primeira",
|
|
467
|
+
'grid.last': "Última",
|
|
451
468
|
'chart.other': "Outros",
|
|
452
469
|
'chart.label': "Etiqueta",
|
|
453
470
|
'chart.bubble': "Bolhas",
|
package/src/i18n/ru.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const ru: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Позиция столбца {column}",
|
|
449
449
|
'grid.column_width': "Ширина столбца {column} в пикселях",
|
|
450
450
|
'grid.default_aggregates': "Показываются всегда: {list}.",
|
|
451
|
+
'reset.link': "Забыли пароль?",
|
|
452
|
+
'reset.title': "Сброс пароля",
|
|
453
|
+
'reset.intro': "Введите имя пользователя или адрес электронной почты. Если он принадлежит учётной записи, мы отправим ссылку для выбора нового пароля.",
|
|
454
|
+
'reset.login_label': "Имя пользователя или e-mail",
|
|
455
|
+
'reset.send': "Отправить ссылку",
|
|
456
|
+
'reset.sent': "Если данные совпадают с учётной записью, письмо со ссылкой уже в пути. Ссылка работает один раз в течение 30 минут.",
|
|
457
|
+
'reset.mail_subject': "Выберите новый пароль для {app}",
|
|
458
|
+
'reset.mail_body': "Здравствуйте, #USER#!\n\nКто-то, надеемся, вы, запросил новый пароль для {app}. Откройте эту ссылку в течение 30 минут, чтобы выбрать его:\n\n{link}\n\nЕсли вы этого не запрашивали, просто проигнорируйте письмо: ваш пароль останется прежним.",
|
|
459
|
+
'reset.new_title': "Выберите новый пароль",
|
|
460
|
+
'reset.invalid': "Эта ссылка недействительна (или устарела). Запросите новую.",
|
|
461
|
+
'reset.done': "Пароль изменён. Войдите с новым паролем.",
|
|
462
|
+
'reset.back': "Вернуться ко входу",
|
|
463
|
+
'grid.view_grid': "Сетка",
|
|
464
|
+
'grid.view_row': "Просмотр одной строки",
|
|
465
|
+
'grid.row_of': "Строка {row} из {total}",
|
|
466
|
+
'grid.first': "Первая",
|
|
467
|
+
'grid.last': "Последняя",
|
|
451
468
|
'chart.other': "Прочее",
|
|
452
469
|
'chart.label': "Подпись",
|
|
453
470
|
'chart.bubble': "Пузырьковая",
|
package/src/i18n/sv.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const sv: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Position för {column}",
|
|
449
449
|
'grid.column_width': "Bredd för {column} i pixlar",
|
|
450
450
|
'grid.default_aggregates': "Visas alltid: {list}.",
|
|
451
|
+
'reset.link': "Glömt lösenordet?",
|
|
452
|
+
'reset.title': "Återställ lösenordet",
|
|
453
|
+
'reset.intro': "Ange ditt användarnamn eller din e-postadress. Om det hör till ett konto skickar vi en länk där du kan välja ett nytt lösenord.",
|
|
454
|
+
'reset.login_label': "Användarnamn eller e-post",
|
|
455
|
+
'reset.send': "Skicka länk",
|
|
456
|
+
'reset.sent': "Om det matchar ett konto är ett e-postmeddelande med en länk på väg. Länken fungerar en gång, i 30 minuter.",
|
|
457
|
+
'reset.mail_subject': "Välj ett nytt lösenord för {app}",
|
|
458
|
+
'reset.mail_body': "Hej #USER#,\n\nNågon, förhoppningsvis du, har bett om att få välja ett nytt lösenord för {app}. Öppna den här länken inom 30 minuter för att göra det:\n\n{link}\n\nOm du inte bad om detta kan du strunta i meddelandet: ditt lösenord förblir som det är.",
|
|
459
|
+
'reset.new_title': "Välj ett nytt lösenord",
|
|
460
|
+
'reset.invalid': "Länken är inte (längre) giltig. Be om en ny.",
|
|
461
|
+
'reset.done': "Ditt lösenord har ändrats. Logga in med det nya.",
|
|
462
|
+
'reset.back': "Tillbaka till inloggningen",
|
|
463
|
+
'grid.view_grid': "Rutnät",
|
|
464
|
+
'grid.view_row': "Enradsvy",
|
|
465
|
+
'grid.row_of': "Rad {row} av {total}",
|
|
466
|
+
'grid.first': "Första",
|
|
467
|
+
'grid.last': "Sista",
|
|
451
468
|
'chart.other': "Övrigt",
|
|
452
469
|
'chart.label': "Etikett",
|
|
453
470
|
'chart.bubble': "Bubbla",
|
package/src/i18n/tr.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const tr: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "{column} konumu",
|
|
449
449
|
'grid.column_width': "{column} genişliği (piksel)",
|
|
450
450
|
'grid.default_aggregates': "Her zaman gösterilir: {list}.",
|
|
451
|
+
'reset.link': "Parolanızı mı unuttunuz?",
|
|
452
|
+
'reset.title': "Parolayı sıfırla",
|
|
453
|
+
'reset.intro': "Kullanıcı adınızı veya e-posta adresinizi girin. Bir hesaba aitse, yeni bir parola seçmeniz için bir bağlantı göndereceğiz.",
|
|
454
|
+
'reset.login_label': "Kullanıcı adı veya e-posta",
|
|
455
|
+
'reset.send': "Bağlantı gönder",
|
|
456
|
+
'reset.sent': "Bir hesapla eşleşiyorsa, bağlantı içeren bir e-posta yolda. Bağlantı bir kez ve 30 dakika boyunca çalışır.",
|
|
457
|
+
'reset.mail_subject': "{app} için yeni bir parola seçin",
|
|
458
|
+
'reset.mail_body': "Merhaba #USER#,\n\nBirisi, umarız sizsiniz, {app} için yeni bir parola seçmek istedi. Bunu yapmak için bu bağlantıyı 30 dakika içinde açın:\n\n{link}\n\nBunu siz istemediyseniz bu e-postayı yok sayabilirsiniz: parolanız değişmez.",
|
|
459
|
+
'reset.new_title': "Yeni bir parola seçin",
|
|
460
|
+
'reset.invalid': "Bu bağlantı geçerli değil (artık). Yeni bir tane isteyin.",
|
|
461
|
+
'reset.done': "Parolanız değiştirildi. Yenisiyle oturum açın.",
|
|
462
|
+
'reset.back': "Oturum açmaya dön",
|
|
463
|
+
'grid.view_grid': "Izgara",
|
|
464
|
+
'grid.view_row': "Tek satır görünümü",
|
|
465
|
+
'grid.row_of': "Satır {row} / {total}",
|
|
466
|
+
'grid.first': "İlk",
|
|
467
|
+
'grid.last': "Son",
|
|
451
468
|
'chart.other': "Diğer",
|
|
452
469
|
'chart.label': "Etiket",
|
|
453
470
|
'chart.bubble': "Kabarcık",
|
package/src/i18n/uk.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const uk: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "Позиція стовпця {column}",
|
|
449
449
|
'grid.column_width': "Ширина стовпця {column} у пікселях",
|
|
450
450
|
'grid.default_aggregates': "Показуються завжди: {list}.",
|
|
451
|
+
'reset.link': "Забули пароль?",
|
|
452
|
+
'reset.title': "Скидання пароля",
|
|
453
|
+
'reset.intro': "Введіть ім'я користувача або адресу електронної пошти. Якщо вона належить обліковому запису, ми надішлемо посилання для вибору нового пароля.",
|
|
454
|
+
'reset.login_label': "Ім'я користувача або e-mail",
|
|
455
|
+
'reset.send': "Надіслати посилання",
|
|
456
|
+
'reset.sent': "Якщо дані збігаються з обліковим записом, лист із посиланням уже в дорозі. Посилання працює один раз протягом 30 хвилин.",
|
|
457
|
+
'reset.mail_subject': "Виберіть новий пароль для {app}",
|
|
458
|
+
'reset.mail_body': "Вітаємо, #USER#!\n\nХтось, сподіваємося, ви, попросив новий пароль для {app}. Відкрийте це посилання протягом 30 хвилин, щоб вибрати його:\n\n{link}\n\nЯкщо ви цього не просили, просто проігноруйте лист: ваш пароль залишиться незмінним.",
|
|
459
|
+
'reset.new_title': "Виберіть новий пароль",
|
|
460
|
+
'reset.invalid': "Це посилання недійсне (або застаріло). Попросіть нове.",
|
|
461
|
+
'reset.done': "Пароль змінено. Увійдіть із новим паролем.",
|
|
462
|
+
'reset.back': "Повернутися до входу",
|
|
463
|
+
'grid.view_grid': "Сітка",
|
|
464
|
+
'grid.view_row': "Перегляд одного рядка",
|
|
465
|
+
'grid.row_of': "Рядок {row} з {total}",
|
|
466
|
+
'grid.first': "Перший",
|
|
467
|
+
'grid.last': "Останній",
|
|
451
468
|
'chart.other': "Інше",
|
|
452
469
|
'chart.label': "Підпис",
|
|
453
470
|
'chart.bubble': "Бульбашкова",
|
package/src/i18n/zh.ts
CHANGED
|
@@ -448,6 +448,23 @@ export const zh: Record<MessageKey, string> = {
|
|
|
448
448
|
'grid.column_position': "{column} 的位置",
|
|
449
449
|
'grid.column_width': "{column} 的宽度(像素)",
|
|
450
450
|
'grid.default_aggregates': "始终显示:{list}。",
|
|
451
|
+
'reset.link': "忘记密码?",
|
|
452
|
+
'reset.title': "重置密码",
|
|
453
|
+
'reset.intro': "请输入用户名或电子邮件地址。如果它属于某个账户,我们会发送一个链接,供你设置新密码。",
|
|
454
|
+
'reset.login_label': "用户名或电子邮件",
|
|
455
|
+
'reset.send': "发送链接",
|
|
456
|
+
'reset.sent': "如果与某个账户匹配,一封带链接的邮件已在路上。该链接只能使用一次,有效期 30 分钟。",
|
|
457
|
+
'reset.mail_subject': "为 {app} 设置新密码",
|
|
458
|
+
'reset.mail_body': "#USER#,你好:\n\n有人(希望是你)请求为 {app} 设置新密码。请在 30 分钟内打开以下链接进行设置:\n\n{link}\n\n如果不是你本人请求,请忽略此邮件,你的密码不会改变。",
|
|
459
|
+
'reset.new_title': "设置新密码",
|
|
460
|
+
'reset.invalid': "此链接无效或已过期。请重新申请。",
|
|
461
|
+
'reset.done': "密码已更改。请使用新密码登录。",
|
|
462
|
+
'reset.back': "返回登录",
|
|
463
|
+
'grid.view_grid': "网格",
|
|
464
|
+
'grid.view_row': "单行视图",
|
|
465
|
+
'grid.row_of': "第 {row} 行,共 {total} 行",
|
|
466
|
+
'grid.first': "第一条",
|
|
467
|
+
'grid.last': "最后一条",
|
|
451
468
|
'chart.other': "其他",
|
|
452
469
|
'chart.label': "标签",
|
|
453
470
|
'chart.bubble': "气泡图",
|
package/src/i18n.ts
CHANGED
|
@@ -475,6 +475,23 @@ const en = {
|
|
|
475
475
|
'grid.column_position': 'Position of {column}',
|
|
476
476
|
'grid.column_width': 'Width of {column} in pixels',
|
|
477
477
|
'grid.default_aggregates': 'Always shown: {list}.',
|
|
478
|
+
'reset.link': "Forgot password?",
|
|
479
|
+
'reset.title': "Reset your password",
|
|
480
|
+
'reset.intro': "Enter your user name or e-mail address. If it belongs to an account, we'll send you a link to choose a new password.",
|
|
481
|
+
'reset.login_label': "User name or e-mail",
|
|
482
|
+
'reset.send': "Send link",
|
|
483
|
+
'reset.sent': "If that matches an account, an e-mail with a link is on its way. The link works once, for 30 minutes.",
|
|
484
|
+
'reset.mail_subject': "Choose a new password for {app}",
|
|
485
|
+
'reset.mail_body': "Hello #USER#,\n\nSomeone, hopefully you, asked to choose a new password for {app}. Open this link within 30 minutes to do so:\n\n{link}\n\nIf you didn't ask for this, you can ignore this e-mail: your password stays as it is.",
|
|
486
|
+
'reset.new_title': "Choose a new password",
|
|
487
|
+
'reset.invalid': "This link isn't valid (any more). Ask for a new one.",
|
|
488
|
+
'reset.done': "Your password has been changed. Sign in with the new one.",
|
|
489
|
+
'reset.back': "Back to sign-in",
|
|
490
|
+
'grid.view_grid': 'Grid',
|
|
491
|
+
'grid.view_row': 'Single row view',
|
|
492
|
+
'grid.row_of': 'Row {row} of {total}',
|
|
493
|
+
'grid.first': 'First',
|
|
494
|
+
'grid.last': 'Last',
|
|
478
495
|
'chart.other': 'Other',
|
|
479
496
|
'chart.label': 'Label',
|
|
480
497
|
'chart.bubble': 'Bubble',
|
|
@@ -1105,6 +1122,23 @@ const nl: Record<MessageKey, string> = {
|
|
|
1105
1122
|
'grid.column_position': 'Positie van {column}',
|
|
1106
1123
|
'grid.column_width': 'Breedte van {column} in pixels',
|
|
1107
1124
|
'grid.default_aggregates': 'Altijd getoond: {list}.',
|
|
1125
|
+
'reset.link': "Wachtwoord vergeten?",
|
|
1126
|
+
'reset.title': "Wachtwoord opnieuw instellen",
|
|
1127
|
+
'reset.intro': "Vul je gebruikersnaam of e-mailadres in. Hoort het bij een account, dan sturen we je een link om een nieuw wachtwoord te kiezen.",
|
|
1128
|
+
'reset.login_label': "Gebruikersnaam of e-mail",
|
|
1129
|
+
'reset.send': "Link versturen",
|
|
1130
|
+
'reset.sent': "Als dit bij een account hoort, is er een e-mail met een link onderweg. De link werkt één keer, 30 minuten lang.",
|
|
1131
|
+
'reset.mail_subject': "Kies een nieuw wachtwoord voor {app}",
|
|
1132
|
+
'reset.mail_body': "Hallo #USER#,\n\nIemand, hopelijk jij, heeft gevraagd om een nieuw wachtwoord voor {app}. Open binnen 30 minuten deze link om er een te kiezen:\n\n{link}\n\nHeb je dit niet gevraagd, dan kun je deze e-mail negeren: je wachtwoord blijft zoals het is.",
|
|
1133
|
+
'reset.new_title': "Kies een nieuw wachtwoord",
|
|
1134
|
+
'reset.invalid': "Deze link is niet (meer) geldig. Vraag een nieuwe aan.",
|
|
1135
|
+
'reset.done': "Je wachtwoord is gewijzigd. Meld je aan met het nieuwe.",
|
|
1136
|
+
'reset.back': "Terug naar aanmelden",
|
|
1137
|
+
'grid.view_grid': 'Raster',
|
|
1138
|
+
'grid.view_row': 'Enkele rij',
|
|
1139
|
+
'grid.row_of': 'Rij {row} van {total}',
|
|
1140
|
+
'grid.first': 'Eerste',
|
|
1141
|
+
'grid.last': 'Laatste',
|
|
1108
1142
|
'chart.other': 'Overig',
|
|
1109
1143
|
'chart.label': 'Label',
|
|
1110
1144
|
'chart.bubble': 'Bellen',
|
package/src/icons.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { readdirSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { html } from './html.ts';
|
|
5
|
+
import { staticUrl } from './static-version.ts';
|
|
5
6
|
|
|
6
7
|
// Names available in public/icons.svg (for navigation entries, cards, builder).
|
|
7
8
|
export const ICONS = [
|
|
@@ -75,7 +76,7 @@ export const isIcon = (value: string | null | undefined) => !!iconParts(value);
|
|
|
75
76
|
export function icon(name: string | null | undefined, cls = 'icon') {
|
|
76
77
|
const p = iconParts(name);
|
|
77
78
|
if (!p) return '';
|
|
78
|
-
const href = p.set === 'pgkiln' ?
|
|
79
|
+
const href = p.set === 'pgkiln' ? `${staticUrl('icons.svg')}#${p.name}` : `/static/icon/${p.name}.svg?v=${LUCIDE_VERSION}#i`;
|
|
79
80
|
return html`<svg class="${[cls, ...p.classes].join(' ')}" aria-hidden="true" focusable="false"><use href="${href}"></use></svg>`;
|
|
80
81
|
}
|
|
81
82
|
|
package/src/layout.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { html, type Raw } from './html.ts';
|
|
2
|
+
import { staticUrl } from './static-version.ts';
|
|
2
3
|
|
|
3
4
|
/** Attributes of <html>: language, text direction and a fixed theme (light/dark; absent = follow the OS). */
|
|
4
5
|
export interface RootAttrs {
|
|
@@ -17,8 +18,8 @@ export function documentShell(title: string, body: Raw, bodyClass = '', data: Re
|
|
|
17
18
|
<meta charset="utf-8">
|
|
18
19
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
19
20
|
<title>${title}</title>
|
|
20
|
-
<link rel="stylesheet" href="
|
|
21
|
-
<script src="
|
|
21
|
+
<link rel="stylesheet" href="${staticUrl('app.css')}">
|
|
22
|
+
<script src="${staticUrl('app.js')}" defer></script>
|
|
22
23
|
${head}
|
|
23
24
|
</head>
|
|
24
25
|
<body class="${bodyClass}"${attrs}>
|
package/src/metadata.ts
CHANGED
|
@@ -72,6 +72,10 @@ export interface App {
|
|
|
72
72
|
local_login: boolean;
|
|
73
73
|
/** days a "Remember me" sign-in lasts; null = off */
|
|
74
74
|
remember_me_days: number | null;
|
|
75
|
+
/** (087) the page that replaces the built-in sign-in page */
|
|
76
|
+
login_page: number | null;
|
|
77
|
+
/** (087) offer "Forgot password?" (with an enabled mail service) */
|
|
78
|
+
password_reset: boolean;
|
|
75
79
|
/** (067) session sharing group (src/sharedlogin.ts) */
|
|
76
80
|
session_group: string | null;
|
|
77
81
|
/** LDAP directories the password form checks after local accounts */
|
|
@@ -136,13 +140,13 @@ export interface Region {
|
|
|
136
140
|
title: string | null;
|
|
137
141
|
/** (0.31, 073) optional, unique on the page */
|
|
138
142
|
static_id?: string | null;
|
|
139
|
-
type: 'report' | 'form' | 'chart' | 'cards' | 'static' | 'grid' | 'calendar' | 'dynamic' | 'facets' | 'tasks' | 'workflows' | 'map' | 'tree' | 'template_component' | 'smart_filters' | 'display_selector' | 'list' | 'data_reporter' | 'ai_assistant' | 'plugin' | 'url' | 'search';
|
|
143
|
+
type: 'login' | 'report' | 'form' | 'chart' | 'cards' | 'static' | 'grid' | 'calendar' | 'dynamic' | 'facets' | 'tasks' | 'workflows' | 'map' | 'tree' | 'template_component' | 'smart_filters' | 'display_selector' | 'list' | 'data_reporter' | 'ai_assistant' | 'plugin' | 'url' | 'search';
|
|
140
144
|
source: string | null;
|
|
141
145
|
table_name: string | null;
|
|
142
146
|
pk_column: string | null;
|
|
143
147
|
pk_item: string | null;
|
|
144
148
|
columns: number;
|
|
145
|
-
template: 'standard' | 'plain' | 'collapsible';
|
|
149
|
+
template: 'standard' | 'plain' | 'collapsible' | 'hero' | 'title_bar' | 'buttons_container' | 'inline_dialog' | 'inline_drawer' | 'wizard';
|
|
146
150
|
condition: string | null;
|
|
147
151
|
authz: string | null;
|
|
148
152
|
config: Record<string, any>;
|
|
@@ -323,6 +327,8 @@ export interface Page extends PageSummary {
|
|
|
323
327
|
static_includes: StaticInclude[];
|
|
324
328
|
/** (077) ask before leaving the page with unsaved changes */
|
|
325
329
|
warn_unsaved: boolean;
|
|
330
|
+
/** (087) 'minimal': no header and navigation */
|
|
331
|
+
page_template: 'standard' | 'minimal';
|
|
326
332
|
}
|
|
327
333
|
|
|
328
334
|
/** The static files a list of names refers to, in that order, with their version. */
|
|
@@ -338,7 +344,7 @@ const agg = (table: string, fk: string, parent: string, appId: string) =>
|
|
|
338
344
|
// No caching on purpose: edits made in the builder show up on the next request.
|
|
339
345
|
export async function loadApp(alias: string) {
|
|
340
346
|
return runtime.one<App>(
|
|
341
|
-
`select a.id, a.alias, a.name, a.home_page, a.authentication, a.access_control, a.sso_providers, a.local_login, a.remember_me_days, a.session_group, a.ldap_directories, a.header_name, a.header_auto_create, a.logout_url, a.db_auth_roles, a.db_auth_member_of, a.custom_auth_function, a.custom_auth_code, a.custom_auth_post_code, a.nav_list, a.navbar_list, a.pwa, a.pwa_short_name, a.pwa_icon is not null as pwa_has_icon, a.pwa_offline_pages, a.pwa_offline_submit, a.pwa_push, a.db_role, a.debug, a.debug_level, a.theme,
|
|
347
|
+
`select a.id, a.alias, a.name, a.home_page, a.authentication, a.access_control, a.sso_providers, a.local_login, a.remember_me_days, a.login_page, a.password_reset, a.session_group, a.ldap_directories, a.header_name, a.header_auto_create, a.logout_url, a.db_auth_roles, a.db_auth_member_of, a.custom_auth_function, a.custom_auth_code, a.custom_auth_post_code, a.nav_list, a.navbar_list, a.pwa, a.pwa_short_name, a.pwa_icon is not null as pwa_has_icon, a.pwa_offline_pages, a.pwa_offline_submit, a.pwa_push, a.db_role, a.debug, a.debug_level, a.theme,
|
|
342
348
|
a.availability, a.availability_message, a.availability_users, a.feedback, a.error_function, a.session_warning_seconds,
|
|
343
349
|
a.language, a.languages, a.language_from, a.date_format, a.timestamp_format, a.time_zone, a.time_zone_auto, a.currency,
|
|
344
350
|
coalesce((select jsonb_agg(jsonb_build_object('name', l.name, 'query', l.query, 'rest_source', l.rest_source)) from meta.lov l where l.app_id = a.id), '[]') as lovs,
|
|
@@ -364,7 +370,7 @@ export async function loadApp(alias: string) {
|
|
|
364
370
|
export async function loadPage(appId: number, pageNo: number) {
|
|
365
371
|
return runtime.one<Page>(
|
|
366
372
|
`select p.id, p.app_id, p.page_no, p.name, p.title, p.requires_auth, p.parent_page, p.mode,
|
|
367
|
-
p.protection, p.authz, p.warn_unsaved,
|
|
373
|
+
p.protection, p.authz, p.warn_unsaved, p.page_template,
|
|
368
374
|
${agg('meta.region', 'page_id', 'p', 'p.app_id')} as regions,
|
|
369
375
|
${agg('meta.item', 'page_id', 'p', 'p.app_id')} as items,
|
|
370
376
|
${agg('meta.button', 'page_id', 'p', 'p.app_id')} as buttons,
|
package/src/runtime/devbar.ts
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
// builder session's token, and every builder request still needs that one.
|
|
11
11
|
//
|
|
12
12
|
// The toolbar links to the App Builder, the application and the page in the
|
|
13
|
-
// Page Designer,
|
|
13
|
+
// Page Designer, (sprint 59) Quick edit: click a region, item or button of the
|
|
14
|
+
// page to open it in the Page Designer (app.js; GET /builder/pages/:id/quick), shows the session state (GET /a/:alias/:page/devbar/session),
|
|
14
15
|
// clears the page's items, and switches debug messages on for this session
|
|
15
16
|
// only (POST …/devbar/debug; meta.session.state.__DEBUG_LEVEL, read by
|
|
16
17
|
// loadContext).
|
|
@@ -84,6 +85,7 @@ export function devbarHtml(ctx: PageContext): Raw | '' {
|
|
|
84
85
|
<li><a href="${BUILDER}">App Builder</a></li>
|
|
85
86
|
<li><a href="${BUILDER}/apps/${ctx.app.id}">Application ${ctx.app.id}</a></li>
|
|
86
87
|
<li><a href="${BUILDER}/pages/${ctx.page.id}">Edit page ${page}</a></li>
|
|
88
|
+
<li><button type="button" class="devbar-quick" data-quick-edit="${BUILDER}/pages/${ctx.page.id}" aria-pressed="false" hidden>Quick edit</button></li>
|
|
87
89
|
<li><a href="${ctx.base}/${page}/devbar/session">Session</a></li>
|
|
88
90
|
<li><form method="post" action="${ctx.base}/${page}/devbar/debug" class="devbar-debug">
|
|
89
91
|
<input type="hidden" name="__csrf" value="${ctx.session.csrf_token}">
|
|
@@ -8,7 +8,12 @@ import type { Region } from '../metadata.ts';
|
|
|
8
8
|
// named after the region), or "display_selector": "Tab name" (regions with
|
|
9
9
|
// the same name share one tab, e.g. a smart filters region and its report).
|
|
10
10
|
// They stay where the page puts them.
|
|
11
|
-
// config: {"style": "tabs" | "select", "show_all": true, "remember": true}
|
|
11
|
+
// config: {"style": "tabs" | "select" | "carousel", "show_all": true, "remember": true, "interval": 6}
|
|
12
|
+
//
|
|
13
|
+
// (088) "carousel" (APEX: the Carousel region template): one region at a
|
|
14
|
+
// time with Previous and Next buttons and a dot per region; "interval"
|
|
15
|
+
// seconds turn to the next one by themselves (not while the pointer or the
|
|
16
|
+
// focus is in it, nor for users who prefer reduced motion). No "Show all".
|
|
12
17
|
//
|
|
13
18
|
// Without JavaScript the bar is a list of links to the regions (#R<id>) and
|
|
14
19
|
// every region shows. app.js turns it into ARIA tabs (arrow keys, Home,
|
|
@@ -40,12 +45,14 @@ export function renderDisplaySelector(ctx: PageContext, r: Region): Raw {
|
|
|
40
45
|
const t = ctx.locale.t;
|
|
41
46
|
const tabs = selectorTabs(ctx, (x) => `${t('rds.region')} ${x.id}`);
|
|
42
47
|
if (!tabs.length) return html`<p class="muted">${t('rds.empty')}</p>`;
|
|
43
|
-
const style = r.config.style === 'select' ? 'select' : 'tabs';
|
|
44
|
-
const showAll = r.config.show_all !== false;
|
|
48
|
+
const style = r.config.style === 'select' ? 'select' : r.config.style === 'carousel' ? 'carousel' : 'tabs';
|
|
49
|
+
const showAll = style !== 'carousel' && r.config.show_all !== false;
|
|
50
|
+
const interval = style === 'carousel' ? Math.max(0, Math.min(120, Number(r.config.interval) || 0)) : 0;
|
|
45
51
|
const id = `rds${r.id}`;
|
|
46
52
|
const label = r.title || t('rds.label');
|
|
47
53
|
const targets = (x: SelectorTab) => x.regions.map((n) => `R${n}`).join(' ');
|
|
48
|
-
return html`<nav class="rds rds-${style}" id="${id}" aria-label="${label}" data-rds="${r.id}"${r.config.remember !== false ? html` data-rds-remember` : ''}>
|
|
54
|
+
return html`<nav class="rds rds-${style}" id="${id}" aria-label="${label}" data-rds="${r.id}"${r.config.remember !== false ? html` data-rds-remember` : ''}${interval ? html` data-rds-interval="${interval}"` : ''}${style === 'carousel' ? html` aria-roledescription="carousel"` : ''}>
|
|
55
|
+
${style === 'carousel' ? html`<button type="button" class="btn rds-prev" data-rds-step="-1" aria-label="${t('report.previous')}" hidden>‹</button>` : ''}
|
|
49
56
|
<ul class="rds-list">
|
|
50
57
|
${showAll ? html`<li><a class="rds-tab" id="${id}_all" href="#${id}" data-rds-all>${t('rds.show_all')}</a></li>` : ''}
|
|
51
58
|
${tabs.map((x, i) => html`<li><a class="rds-tab" id="${id}_${i}" href="#R${x.regions[0]}" data-rds-target="${targets(x)}">${x.label}</a></li>`)}
|
|
@@ -57,5 +64,6 @@ export function renderDisplaySelector(ctx: PageContext, r: Region): Raw {
|
|
|
57
64
|
${tabs.map((x, i) => html`<option value="${i}">${x.label}</option>`)}
|
|
58
65
|
</select></label>`
|
|
59
66
|
: ''}
|
|
67
|
+
${style === 'carousel' ? html`<button type="button" class="btn rds-next" data-rds-step="1" aria-label="${t('report.next')}" hidden>›</button>` : ''}
|
|
60
68
|
</nav>`;
|
|
61
69
|
}
|