@opengis/fastify-table 2.6.1 → 2.6.2
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/README.md +86 -86
- package/dist/functions.js +20 -20
- package/dist/helper.d.ts +1 -20
- package/dist/helper.d.ts.map +1 -1
- package/dist/module/core/cls/constraint_action.json +9 -9
- package/dist/module/core/cls/constraint_matchtype.json +5 -5
- package/dist/module/core/cls/constraint_type_full.json +17 -17
- package/dist/module/core/cls/core.user_type.json +13 -13
- package/dist/module/core/pt/schemaItem.pt.hbs +17 -17
- package/dist/module/core/select/core.user_mentioned.sql +1 -1
- package/dist/script/adduser +14 -14
- package/dist/script/dump.js +48 -48
- package/dist/script/dump.ts +216 -216
- package/dist/script/migrate.ts +23 -23
- package/dist/server/helpers/core/badge.js +1 -1
- package/dist/server/helpers/list/descriptionList.js +8 -8
- package/dist/server/helpers/list/tableList.js +4 -4
- package/dist/server/helpers/list/utils/button.js +1 -1
- package/dist/server/helpers/list/utils/buttonDel.js +3 -3
- package/dist/server/helpers/list/utils/buttonEdit.js +3 -3
- package/dist/server/helpers/utils/button.js +1 -1
- package/dist/server/helpers/utils/buttonAdd.js +15 -15
- package/dist/server/helpers/utils/buttonDel.js +11 -11
- package/dist/server/helpers/utils/buttonEdit.js +3 -3
- package/dist/server/migrations/0.sql +99 -99
- package/dist/server/migrations/1_users.sql +145 -145
- package/dist/server/migrations/cls.sql +105 -105
- package/dist/server/migrations/context.sql +136 -136
- package/dist/server/migrations/dictionaries.sql +78 -78
- package/dist/server/migrations/oauth.sql +79 -79
- package/dist/server/migrations/properties.sql +115 -115
- package/dist/server/migrations/roles.sql +201 -200
- package/dist/server/migrations/template.sql +43 -43
- package/dist/server/plugins/access/funcs/getUserPermissions.js +7 -7
- package/dist/server/plugins/auth/funcs/authorizeUser.js +4 -4
- package/dist/server/plugins/auth/funcs/getQuery.js +20 -20
- package/dist/server/plugins/crud/funcs/dataUpdate.js +7 -7
- package/dist/server/plugins/crud/funcs/getAccess.js +14 -14
- package/dist/server/plugins/crud/funcs/utils/getInsertQuery.js +6 -6
- package/dist/server/plugins/crud/funcs/utils/logChanges.js +18 -18
- package/dist/server/plugins/grpc/utils/convertp.proto +143 -143
- package/dist/server/plugins/grpc/utils/htmlTemplate.js +10 -10
- package/dist/server/plugins/grpc/utils/office2pdf.proto +13 -13
- package/dist/server/plugins/metric/loggerSystem.js +1 -1
- package/dist/server/plugins/pg/funcs/autoIndex.js +5 -5
- package/dist/server/plugins/pg/funcs/getMeta.js +15 -15
- package/dist/server/plugins/pg/funcs/init.js +36 -36
- package/dist/server/plugins/sqlite/funcs/init.js +22 -22
- package/dist/server/plugins/table/funcs/getData.js +13 -13
- package/dist/server/plugins/table/funcs/getFilterSQL/util/getCustomQuery.js +1 -1
- package/dist/server/plugins/table/funcs/getSelect.js +1 -1
- package/dist/server/plugins/table/funcs/gisIRColumn.js +3 -3
- package/dist/server/plugins/usercls/index.js +2 -2
- package/dist/server/routes/access/controllers/access.group.js +6 -6
- package/dist/server/routes/access/controllers/access.group.post.js +3 -3
- package/dist/server/routes/access/controllers/access.interface.js +14 -14
- package/dist/server/routes/access/controllers/access.user.js +6 -6
- package/dist/server/routes/auth/controllers/2factor/providers/totp.js +5 -5
- package/dist/server/routes/auth/controllers/2factor/qrcode.js +1 -1
- package/dist/server/routes/auth/controllers/2factor/recovery.js +1 -1
- package/dist/server/routes/auth/controllers/2factor/verify.js +1 -1
- package/dist/server/routes/auth/controllers/core/getUserInfo.js +33 -33
- package/dist/server/routes/auth/controllers/core/passwordRecovery.js +1 -1
- package/dist/server/routes/auth/controllers/core/registration.js +2 -2
- package/dist/server/routes/auth/controllers/page/login2faTemplate.js +1 -1
- package/dist/server/routes/crud/functions/deleteTableRelation.js +9 -9
- package/dist/server/routes/crud/functions/updateTableRelation.js +9 -9
- package/dist/server/routes/file/controllers/resizeAll.js +7 -7
- package/dist/server/routes/grpc/controllers/file2geojson.js +13 -13
- package/dist/server/routes/menu/controllers/getMenu.js +9 -9
- package/dist/server/routes/notifications/controllers/readNotifications.js +4 -4
- package/dist/server/routes/notifications/controllers/userNotifications.js +3 -3
- package/dist/server/routes/table/controllers/card.js +1 -1
- package/dist/server/routes/table/controllers/filter.js +6 -6
- package/dist/server/routes/table/controllers/form.js +1 -1
- package/dist/server/routes/table/controllers/getFormByTable.js +6 -6
- package/dist/server/routes/table/controllers/suggest.js +10 -10
- package/dist/server/routes/table/controllers/tableData.js +2 -2
- package/dist/server/routes/table/controllers/tableInfo.js +10 -10
- package/dist/server/routes/table/functions/getData.js +13 -13
- package/dist/server/routes/widget/controllers/widget.get.js +33 -33
- package/dist/server/routes/widget/controllers/widget.set.js +4 -4
- package/dist/server/routes/widget/services/widget.entity.get.js +33 -33
- package/dist/server/routes/widget/services/widget.entity.set.js +4 -4
- package/dist/server/templates/page/2factor-recovery.html +101 -101
- package/dist/server/templates/page/2factor.html +140 -140
- package/dist/server/templates/page/login.html +90 -90
- package/dist/server/templates/pt/recovery-codes-email-template.hbs +12 -12
- package/dist/server/templates/pt/recovery-password-email-template.html +20 -20
- package/package.json +99 -99
- package/dist/module/core/select/core.user_mentioned.json +0 -3
|
@@ -153,12 +153,12 @@ export default async function filterAPI(req, reply, iscalled) {
|
|
|
153
153
|
fields?.find?.((item) => item?.name === el.name))
|
|
154
154
|
.map(async (el) => {
|
|
155
155
|
const data = await pg
|
|
156
|
-
.queryCache(`select array[
|
|
157
|
-
min(${el.name}),
|
|
158
|
-
percentile_disc(0.25) within group (order by ${el.name}),
|
|
159
|
-
percentile_disc(0.5) within group (order by ${el.name}),
|
|
160
|
-
percentile_disc(0.75) within group (order by ${el.name}),
|
|
161
|
-
max(${el.name})
|
|
156
|
+
.queryCache(`select array[
|
|
157
|
+
min(${el.name}),
|
|
158
|
+
percentile_disc(0.25) within group (order by ${el.name}),
|
|
159
|
+
percentile_disc(0.5) within group (order by ${el.name}),
|
|
160
|
+
percentile_disc(0.75) within group (order by ${el.name}),
|
|
161
|
+
max(${el.name})
|
|
162
162
|
] as range from ${table} where ${tableQuery || "1=1"}`, { table })
|
|
163
163
|
.then((res) => {
|
|
164
164
|
if (res.timeout) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NotFoundError } from "../../../../errors.js";
|
|
2
2
|
import { applyHook, getTemplate, pgClients } from "../../../../utils.js";
|
|
3
|
-
const q = `select property_key as key, property_json as json, property_int as int,
|
|
3
|
+
const q = `select property_key as key, property_json as json, property_int as int,
|
|
4
4
|
property_text as text from admin.properties where 1=1`;
|
|
5
5
|
async function getSettings({ pg }) {
|
|
6
6
|
const rows = pg?.pk?.["admin.properties"]
|
|
@@ -9,12 +9,12 @@ import getTemplate from "../../../plugins/table/funcs/getTemplate.js";
|
|
|
9
9
|
import getEditData from "../../crud/controllers/table.js";
|
|
10
10
|
import formatSchema from "./utils/formatSchema.js";
|
|
11
11
|
import { ForbiddenError, NotFoundError, UnauthorizedError, } from "../../../../errors.js";
|
|
12
|
-
const q = `select
|
|
13
|
-
property_key as key,
|
|
14
|
-
property_json as json,
|
|
15
|
-
property_int as int,
|
|
16
|
-
property_text as text
|
|
17
|
-
from admin.properties
|
|
12
|
+
const q = `select
|
|
13
|
+
property_key as key,
|
|
14
|
+
property_json as json,
|
|
15
|
+
property_int as int,
|
|
16
|
+
property_text as text
|
|
17
|
+
from admin.properties
|
|
18
18
|
where 1=1`;
|
|
19
19
|
export default async function getForm({ pg = pgClients.client, params, user = {}, query = {}, method, }, reply) {
|
|
20
20
|
const time = Date.now();
|
|
@@ -47,13 +47,13 @@ export async function getTableColumnMeta({ table, template, column, selectName,
|
|
|
47
47
|
: `id in ( with cc(id, text) as (${select}) select id from cc where (lower("text") ~ $1) )`;
|
|
48
48
|
const counts = pg.queryCache
|
|
49
49
|
? await pg
|
|
50
|
-
.queryCache(`SELECT json_object_agg( (
|
|
51
|
-
SELECT
|
|
52
|
-
nspname
|
|
53
|
-
FROM
|
|
54
|
-
pg_namespace
|
|
55
|
-
WHERE
|
|
56
|
-
oid = relnamespace
|
|
50
|
+
.queryCache(`SELECT json_object_agg( (
|
|
51
|
+
SELECT
|
|
52
|
+
nspname
|
|
53
|
+
FROM
|
|
54
|
+
pg_namespace
|
|
55
|
+
WHERE
|
|
56
|
+
oid = relnamespace
|
|
57
57
|
) || '.' || relname, reltuples::bigint) FROM pg_class WHERE relkind = 'r'`)
|
|
58
58
|
.then((r) => r.rows[0].json_object_agg)
|
|
59
59
|
: {};
|
|
@@ -345,11 +345,11 @@ export default async function suggest(req) {
|
|
|
345
345
|
const whereQuery = where.filter(Boolean).join(" and ") || "true";
|
|
346
346
|
args.push(Math.min(query.limit || meta.limit || limit, limit));
|
|
347
347
|
let sqlSuggest = tableName && column
|
|
348
|
-
? `${meta.original}
|
|
349
|
-
WHERE EXISTS ( SELECT 1 FROM ${tableName} o
|
|
348
|
+
? `${meta.original}
|
|
349
|
+
WHERE EXISTS ( SELECT 1 FROM ${tableName} o
|
|
350
350
|
WHERE ${pg.pgType[dataTypeID]?.includes("[]")
|
|
351
351
|
? `c.id = ANY(o."${column}")`
|
|
352
|
-
: `o."${column}" IS NOT DISTINCT FROM c.id`} ) and ${whereQuery || "true"}
|
|
352
|
+
: `o."${column}" IS NOT DISTINCT FROM c.id`} ) and ${whereQuery || "true"}
|
|
353
353
|
${order} LIMIT $${args.length}::int`
|
|
354
354
|
: `with c(id,text) as ( ${meta.original} where ${whereQuery} ${order}) select * from c LIMIT $${args.length}::int`;
|
|
355
355
|
if (sqlSuggest.includes("{{parent}}")) {
|
|
@@ -3,8 +3,8 @@ import getData from "../functions/getData.js";
|
|
|
3
3
|
export default async function tableData(req, reply, called) {
|
|
4
4
|
const { user, params, headers, query, pg = pgClients.client } = req;
|
|
5
5
|
// це можна від користувача
|
|
6
|
-
const q = `select attr,
|
|
7
|
-
(select rule_values from admin.account_grants where account_id in (select account_id from admin.account_users where user_uid=$2) and rule_id=r.rule_id limit 1)
|
|
6
|
+
const q = `select attr,
|
|
7
|
+
(select rule_values from admin.account_grants where account_id in (select account_id from admin.account_users where user_uid=$2) and rule_id=r.rule_id limit 1)
|
|
8
8
|
from admin.rules r where (select route_id from admin.routes where $1 in (alias,table_name)) = any(routes)`;
|
|
9
9
|
const { rows = [] } = pg?.pk?.["admin.rules"] && pg?.pk?.["admin.account_grants"] && user?.uid
|
|
10
10
|
? await pg.query(q, [params.table, user.uid])
|
|
@@ -59,21 +59,21 @@ export default async function tableInfo({ pg = pgClients.client, params, query =
|
|
|
59
59
|
if (!metaInfoCols.length) {
|
|
60
60
|
throw BadRequestError("invalid meta info: columns not found");
|
|
61
61
|
}
|
|
62
|
-
const q = `select ${pk ? `"${pk}" as id,` : ""}
|
|
63
|
-
${metaInfoCols.join(",")}
|
|
64
|
-
from (select * from ${table} where ${sqlTable ? "true" : where.join(" and ") || "true"}) t
|
|
65
|
-
${sqlTable}
|
|
66
|
-
${cardSqlTable}
|
|
67
|
-
where ${where.join(" and ") || "true"}
|
|
62
|
+
const q = `select ${pk ? `"${pk}" as id,` : ""}
|
|
63
|
+
${metaInfoCols.join(",")}
|
|
64
|
+
from (select * from ${table} where ${sqlTable ? "true" : where.join(" and ") || "true"}) t
|
|
65
|
+
${sqlTable}
|
|
66
|
+
${cardSqlTable}
|
|
67
|
+
where ${where.join(" and ") || "true"}
|
|
68
68
|
limit 1`.replace(/{{uid}}/g, uid);
|
|
69
69
|
if (query.sql === "1") {
|
|
70
70
|
return q;
|
|
71
71
|
}
|
|
72
72
|
const { rows } = await pg.query(q, [params.id]);
|
|
73
|
-
const qCount = `select
|
|
74
|
-
count(*)::int as total,
|
|
75
|
-
count(*) FILTER(WHERE ${[interfaceQuery].filter((el) => el).join(" and ") || "true"})::int as filtered
|
|
76
|
-
from ${table} t ${sqlTable}
|
|
73
|
+
const qCount = `select
|
|
74
|
+
count(*)::int as total,
|
|
75
|
+
count(*) FILTER(WHERE ${[interfaceQuery].filter((el) => el).join(" and ") || "true"})::int as filtered
|
|
76
|
+
from ${table} t ${sqlTable}
|
|
77
77
|
where ${[loadTable.query].filter((el) => el).join(" and ") || "true"} `.replace(/{{uid}}/g, uid);
|
|
78
78
|
const { total, filtered } = params.id
|
|
79
79
|
? rows.length
|
|
@@ -355,9 +355,9 @@ export default async function dataAPI({ pg = pgClients.client, params, table, id
|
|
|
355
355
|
interfaceQuery1,
|
|
356
356
|
].filter(Boolean);
|
|
357
357
|
const omitGeometry = !objectId && loadTable?.showGeometryMode === "card";
|
|
358
|
-
const q1 = `select ${pk ? `"${pk}" as id,` : ""}
|
|
359
|
-
${objectId || query?.key ? "*" : columnsParam || sqlColumns || cols || "*"}
|
|
360
|
-
${metaCols}
|
|
358
|
+
const q1 = `select ${pk ? `"${pk}" as id,` : ""}
|
|
359
|
+
${objectId || query?.key ? "*" : columnsParam || sqlColumns || cols || "*"}
|
|
360
|
+
${metaCols}
|
|
361
361
|
${dbColumns.filter((el) => pg.pgType?.[el.dataTypeID] === "geometry")
|
|
362
362
|
.length &&
|
|
363
363
|
!columnsParam &&
|
|
@@ -366,14 +366,14 @@ export default async function dataAPI({ pg = pgClients.client, params, table, id
|
|
|
366
366
|
.filter((el) => pg.pgType?.[el.dataTypeID] === "geometry")
|
|
367
367
|
.map((el) => `st_asgeojson("${el.name.replace(/'/g, "''")}")::json as "${el.name.replace(/'/g, "''")}"`)
|
|
368
368
|
.join(",")}`
|
|
369
|
-
: ""}
|
|
369
|
+
: ""}
|
|
370
370
|
from (select * ${sql
|
|
371
371
|
?.filter((el) => el.inline && !(isExport && el.export === false))
|
|
372
372
|
.map((el) => `,(${el.sql})`)
|
|
373
|
-
.join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable} ) t
|
|
374
|
-
|
|
375
|
-
${objectId ? cardSqlTable : ""}
|
|
376
|
-
where ${where.join(" and ") || "true"}
|
|
373
|
+
.join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable} ) t
|
|
374
|
+
|
|
375
|
+
${objectId ? cardSqlTable : ""}
|
|
376
|
+
where ${where.join(" and ") || "true"}
|
|
377
377
|
${order} limit $${idx.limit}::bigint offset $${idx.offset}::bigint`;
|
|
378
378
|
if (q1.includes("{{uid}}")) {
|
|
379
379
|
args.push(uid || "");
|
|
@@ -458,18 +458,18 @@ export default async function dataAPI({ pg = pgClients.client, params, table, id
|
|
|
458
458
|
type: pg.pgType?.[el.dataTypeID],
|
|
459
459
|
}))
|
|
460
460
|
.filter((el) => ["numeric", "double precision"].includes(el.type) && aggColumns[el.name]);
|
|
461
|
-
const qCount = `select
|
|
462
|
-
count(*)::int as total,
|
|
463
|
-
count(*) FILTER(WHERE ${filterWhere.join(" and ") || "true"})::int as filtered
|
|
461
|
+
const qCount = `select
|
|
462
|
+
count(*)::int as total,
|
|
463
|
+
count(*) FILTER(WHERE ${filterWhere.join(" and ") || "true"})::int as filtered
|
|
464
464
|
${aggregates.length
|
|
465
465
|
? `,${aggregates
|
|
466
466
|
.map((el) => `${aggColumns[el.name]}(${el.name}) FILTER(WHERE ${filterWhere.join(" and ") || "true"}) as ${el.name}`)
|
|
467
467
|
.join(",")}`
|
|
468
|
-
: ""}
|
|
468
|
+
: ""}
|
|
469
469
|
from (select * ${sql
|
|
470
470
|
?.filter((el) => el.inline)
|
|
471
471
|
.map((el) => `,(${el.sql})`)
|
|
472
|
-
.join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable})t
|
|
472
|
+
.join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable})t
|
|
473
473
|
where ${[loadTable?.query, tokenData?.query, accessQuery, contextQuery]
|
|
474
474
|
.filter(Boolean)
|
|
475
475
|
.join(" and ") || "true"} `.replace(/'{{uid}}'/g, `$${idx1.uid}`);
|
|
@@ -39,49 +39,49 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
|
|
|
39
39
|
}
|
|
40
40
|
const sqls = {
|
|
41
41
|
comment: pg.pk["admin.users"]
|
|
42
|
-
? `select communication_id, entity_id, body, subject, c.cdate, c.uid,
|
|
43
|
-
${username} as username, u.login, u.avatar
|
|
44
|
-
from crm.communications c
|
|
45
|
-
left join admin.users u on u.uid=c.uid
|
|
42
|
+
? `select communication_id, entity_id, body, subject, c.cdate, c.uid,
|
|
43
|
+
${username} as username, u.login, u.avatar
|
|
44
|
+
from crm.communications c
|
|
45
|
+
left join admin.users u on u.uid=c.uid
|
|
46
46
|
where entity_id=$1 order by cdate desc`
|
|
47
47
|
: "select communication_id, entity_id, body, subject, cdate, uid from crm.communications where entity_id=$1 order by cdate desc",
|
|
48
|
-
history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
|
|
49
|
-
${username} as username, u.login, u.avatar
|
|
50
|
-
FROM log.table_changes a
|
|
51
|
-
left join admin.users u on a.change_user_id = u.uid
|
|
52
|
-
left join lateral(
|
|
53
|
-
select json_agg(row_to_json(q)) from (
|
|
54
|
-
select change_data_id, entity_key, value_new, value_old from log.table_changes_data
|
|
55
|
-
where change_id=a.change_id
|
|
56
|
-
)q
|
|
57
|
-
)b on 1=1
|
|
58
|
-
where a.change_type != 'INSERT' and b.json_agg is not null and (entity_id=$1 or entity_id in (
|
|
59
|
-
select communication_id from crm.communications where entity_id=$1
|
|
60
|
-
union all select checklist_id from crm.checklists where entity_id=$1
|
|
61
|
-
union all select file_id from crm.files where entity_id=$1)
|
|
62
|
-
)
|
|
48
|
+
history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
|
|
49
|
+
${username} as username, u.login, u.avatar
|
|
50
|
+
FROM log.table_changes a
|
|
51
|
+
left join admin.users u on a.change_user_id = u.uid
|
|
52
|
+
left join lateral(
|
|
53
|
+
select json_agg(row_to_json(q)) from (
|
|
54
|
+
select change_data_id, entity_key, value_new, value_old from log.table_changes_data
|
|
55
|
+
where change_id=a.change_id
|
|
56
|
+
)q
|
|
57
|
+
)b on 1=1
|
|
58
|
+
where a.change_type != 'INSERT' and b.json_agg is not null and (entity_id=$1 or entity_id in (
|
|
59
|
+
select communication_id from crm.communications where entity_id=$1
|
|
60
|
+
union all select checklist_id from crm.checklists where entity_id=$1
|
|
61
|
+
union all select file_id from crm.files where entity_id=$1)
|
|
62
|
+
)
|
|
63
63
|
order by cdate desc limit 100`,
|
|
64
64
|
checklist: pg.pk["admin.users"]
|
|
65
|
-
? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
|
|
66
|
-
FROM crm.checklists c
|
|
67
|
-
left join admin.users u on u.uid=c.uid
|
|
65
|
+
? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
|
|
66
|
+
FROM crm.checklists c
|
|
67
|
+
left join admin.users u on u.uid=c.uid
|
|
68
68
|
where entity_id=$1 order by cdate desc`
|
|
69
69
|
: "SELECT checklist_id, entity_id, subject, is_done, done_date, uid, cdate FROM crm.checklists where entity_id=$1 order by cdate desc",
|
|
70
70
|
file: pg.pk["admin.users"]
|
|
71
|
-
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
72
|
-
${username} as username, u.login, isverified, u.avatar, u.uid as author, file_status
|
|
73
|
-
FROM crm.files c
|
|
74
|
-
left join admin.users u on u.uid=c.uid
|
|
71
|
+
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
72
|
+
${username} as username, u.login, isverified, u.avatar, u.uid as author, file_status
|
|
73
|
+
FROM crm.files c
|
|
74
|
+
left join admin.users u on u.uid=c.uid
|
|
75
75
|
where entity_id=$1 and file_status<>3 order by cdate desc`
|
|
76
|
-
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, uid, cdate, file_type, ismain,
|
|
76
|
+
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, uid, cdate, file_type, ismain,
|
|
77
77
|
isverified, uid as author, file_status FROM crm.files c where entity_id=$1 and file_status<>3 order by cdate desc`,
|
|
78
78
|
gallery: pg.pk["admin.users"]
|
|
79
|
-
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
80
|
-
${username} as username, u.login, u.avatar, isverified, u.avatar, c.uid as author, file_status
|
|
81
|
-
FROM crm.files c
|
|
82
|
-
left join admin.users u on u.uid=c.uid
|
|
79
|
+
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
80
|
+
${username} as username, u.login, u.avatar, isverified, u.avatar, c.uid as author, file_status
|
|
81
|
+
FROM crm.files c
|
|
82
|
+
left join admin.users u on u.uid=c.uid
|
|
83
83
|
where entity_id=$1 and file_status<>3 and ext = any($2) order by cdate desc`
|
|
84
|
-
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, ismain,
|
|
84
|
+
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, ismain,
|
|
85
85
|
isverified, uid as author, file_status FROM crm.files c where entity_id=$1 and file_status<>3 and ext = any($2) order by cdate desc`,
|
|
86
86
|
reaction: "SELECT count(*), reaction_type FROM crm.reactions c where entity_id=$1 group by reaction_type",
|
|
87
87
|
};
|
|
@@ -127,7 +127,7 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
|
|
|
127
127
|
}
|
|
128
128
|
const cdateColumn = columns.find((col) => ["cdate", "created_at"].includes(col.name))?.name;
|
|
129
129
|
const editorDateColumn = columns.find((col) => ["editor_date", "updated_at"].includes(col.name))?.name;
|
|
130
|
-
const q1 = `select ${username} as author, u.login ${cdateColumn ? `,a.${cdateColumn} as cdate` : ""} ${editorDateColumn ? `,a.${editorDateColumn} as editor_date` : ""} from ${tableName} a
|
|
130
|
+
const q1 = `select ${username} as author, u.login ${cdateColumn ? `,a.${cdateColumn} as cdate` : ""} ${editorDateColumn ? `,a.${editorDateColumn} as editor_date` : ""} from ${tableName} a
|
|
131
131
|
left join admin.users u on a.${authorIdColumn}=u.uid where a.${pk}=$1 limit 1`;
|
|
132
132
|
const data = pg.pk["admin.users"] && pk && tableName
|
|
133
133
|
? await pg
|
|
@@ -68,9 +68,9 @@ export default async function widgetSet(req, reply) {
|
|
|
68
68
|
uid: user?.uid,
|
|
69
69
|
});
|
|
70
70
|
if (type === "gallery") {
|
|
71
|
-
await pg.query(`update crm.files set ismain=true
|
|
72
|
-
where entity_id=$1
|
|
73
|
-
and file_id=$2
|
|
71
|
+
await pg.query(`update crm.files set ismain=true
|
|
72
|
+
where entity_id=$1
|
|
73
|
+
and file_id=$2
|
|
74
74
|
and (select count(*) = 0 from crm.files where entity_id=$1 and ismain)`, [objectid, rows[0]?.file_id]);
|
|
75
75
|
}
|
|
76
76
|
return {
|
|
@@ -111,7 +111,7 @@ export default async function widgetSet(req, reply) {
|
|
|
111
111
|
});
|
|
112
112
|
// put w/ out file
|
|
113
113
|
if (type === "gallery" && body?.ismain && id) {
|
|
114
|
-
await pg.query(`update crm.files set ismain=false
|
|
114
|
+
await pg.query(`update crm.files set ismain=false
|
|
115
115
|
where entity_id=$1 and file_id<>$2`, [objectid, id]);
|
|
116
116
|
}
|
|
117
117
|
return reply.status(200).send({
|
|
@@ -36,49 +36,49 @@ export default async function widgetEntityGet({ type, entityType, objectid, uid,
|
|
|
36
36
|
}
|
|
37
37
|
const sqls = {
|
|
38
38
|
comment: pg.pk["admin.users"]
|
|
39
|
-
? `select communication_id, entity_id, body, subject, c.cdate, c.uid,
|
|
40
|
-
${username} as username, u.login, u.avatar
|
|
41
|
-
from crm.communications c
|
|
42
|
-
left join admin.users u on u.uid=c.uid
|
|
39
|
+
? `select communication_id, entity_id, body, subject, c.cdate, c.uid,
|
|
40
|
+
${username} as username, u.login, u.avatar
|
|
41
|
+
from crm.communications c
|
|
42
|
+
left join admin.users u on u.uid=c.uid
|
|
43
43
|
where entity_id=$1 and entity_type=$2 order by cdate desc`
|
|
44
44
|
: "select communication_id, entity_id, body, subject, cdate, uid from crm.communications where entity_id=$1 and entity_type=$2 order by cdate desc",
|
|
45
|
-
history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
|
|
46
|
-
${username} as username, u.login, u.avatar
|
|
47
|
-
FROM log.table_changes a
|
|
48
|
-
left join admin.users u on a.change_user_id = u.uid
|
|
49
|
-
left join lateral(
|
|
50
|
-
select json_agg(row_to_json(q)) from (
|
|
51
|
-
select change_data_id, entity_key, value_new, value_old from log.table_changes_data
|
|
52
|
-
where change_id=a.change_id
|
|
53
|
-
)q
|
|
54
|
-
)b on 1=1
|
|
55
|
-
where a.change_type != 'INSERT' and b.json_agg is not null and ((entity_id=$1 and entity_type=$2) or entity_id in (
|
|
56
|
-
select communication_id from crm.communications where entity_id=$1 and entity_type=$2
|
|
57
|
-
union all select checklist_id from crm.checklists where entity_id=$1 and entity_type=$2
|
|
58
|
-
union all select file_id from crm.files where entity_id=$1 and entity_type=$2)
|
|
59
|
-
)
|
|
45
|
+
history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
|
|
46
|
+
${username} as username, u.login, u.avatar
|
|
47
|
+
FROM log.table_changes a
|
|
48
|
+
left join admin.users u on a.change_user_id = u.uid
|
|
49
|
+
left join lateral(
|
|
50
|
+
select json_agg(row_to_json(q)) from (
|
|
51
|
+
select change_data_id, entity_key, value_new, value_old from log.table_changes_data
|
|
52
|
+
where change_id=a.change_id
|
|
53
|
+
)q
|
|
54
|
+
)b on 1=1
|
|
55
|
+
where a.change_type != 'INSERT' and b.json_agg is not null and ((entity_id=$1 and entity_type=$2) or entity_id in (
|
|
56
|
+
select communication_id from crm.communications where entity_id=$1 and entity_type=$2
|
|
57
|
+
union all select checklist_id from crm.checklists where entity_id=$1 and entity_type=$2
|
|
58
|
+
union all select file_id from crm.files where entity_id=$1 and entity_type=$2)
|
|
59
|
+
)
|
|
60
60
|
order by cdate desc limit 100`,
|
|
61
61
|
checklist: pg.pk["admin.users"]
|
|
62
|
-
? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
|
|
63
|
-
FROM crm.checklists c
|
|
64
|
-
left join admin.users u on u.uid=c.uid
|
|
62
|
+
? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
|
|
63
|
+
FROM crm.checklists c
|
|
64
|
+
left join admin.users u on u.uid=c.uid
|
|
65
65
|
where entity_id=$1 and entity_type=$2 order by cdate desc`
|
|
66
66
|
: "SELECT checklist_id, entity_id, subject, is_done, done_date, uid, cdate FROM crm.checklists where entity_id=$1 and entity_type=$2 order by cdate desc",
|
|
67
67
|
file: pg.pk["admin.users"]
|
|
68
|
-
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
69
|
-
${username} as username, u.login, isverified, u.avatar, u.uid as author, file_status
|
|
70
|
-
FROM crm.files c
|
|
71
|
-
left join admin.users u on u.uid=c.uid
|
|
68
|
+
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
69
|
+
${username} as username, u.login, isverified, u.avatar, u.uid as author, file_status
|
|
70
|
+
FROM crm.files c
|
|
71
|
+
left join admin.users u on u.uid=c.uid
|
|
72
72
|
where entity_id=$1 and entity_type=$2 and file_status<>3 order by cdate desc`
|
|
73
|
-
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, uid, cdate, file_type, ismain,
|
|
73
|
+
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, uid, cdate, file_type, ismain,
|
|
74
74
|
isverified, uid as author, file_status FROM crm.files c where entity_id=$1 and entity_type=$2 and file_status<>3 order by cdate desc`,
|
|
75
75
|
gallery: pg.pk["admin.users"]
|
|
76
|
-
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
77
|
-
${username} as username, u.login, u.avatar, isverified, u.avatar, c.uid as author, file_status
|
|
78
|
-
FROM crm.files c
|
|
79
|
-
left join admin.users u on u.uid=c.uid
|
|
76
|
+
? `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, c.ismain,
|
|
77
|
+
${username} as username, u.login, u.avatar, isverified, u.avatar, c.uid as author, file_status
|
|
78
|
+
FROM crm.files c
|
|
79
|
+
left join admin.users u on u.uid=c.uid
|
|
80
80
|
where entity_id=$1 and entity_type=$2 and file_status<>3 and ext = any($3) order by cdate desc`
|
|
81
|
-
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, ismain,
|
|
81
|
+
: `SELECT file_id, entity_id, entity_type, file_path, uploaded_name, ext, size, c.uid, c.cdate, file_type, ismain,
|
|
82
82
|
isverified, uid as author, file_status FROM crm.files c where entity_id=$1 and entity_type=$2 and file_status<>3 and ext = any($3) order by cdate desc`,
|
|
83
83
|
reaction: "SELECT count(*), reaction_type FROM crm.reactions c where entity_id=$1 and entity_type=$2 group by reaction_type",
|
|
84
84
|
};
|
|
@@ -117,7 +117,7 @@ export default async function widgetEntityGet({ type, entityType, objectid, uid,
|
|
|
117
117
|
}
|
|
118
118
|
const cdateColumn = columns.find((col) => ["cdate", "created_at"].includes(col.name))?.name;
|
|
119
119
|
const editorDateColumn = columns.find((col) => ["editor_date", "updated_at"].includes(col.name))?.name;
|
|
120
|
-
const q1 = `select ${username} as author, u.login ${cdateColumn ? `,a.${cdateColumn} as cdate` : ""} ${editorDateColumn ? `,a.${editorDateColumn} as editor_date` : ""} from ${entityType} a
|
|
120
|
+
const q1 = `select ${username} as author, u.login ${cdateColumn ? `,a.${cdateColumn} as cdate` : ""} ${editorDateColumn ? `,a.${editorDateColumn} as editor_date` : ""} from ${entityType} a
|
|
121
121
|
left join admin.users u on a.${authorIdColumn}=u.uid where a.${pk}=$1 limit 1`;
|
|
122
122
|
const data = pg.pk["admin.users"] && pk && entityType
|
|
123
123
|
? await pg
|
|
@@ -75,9 +75,9 @@ export default async function widgetEntitySet({ type, entityType, objectid, id,
|
|
|
75
75
|
uid,
|
|
76
76
|
}).then((r) => r.rows?.[0]);
|
|
77
77
|
if (type === "gallery") {
|
|
78
|
-
await pg.query(`update crm.files set ismain=true
|
|
79
|
-
where entity_id=$1 and entity_type=$2
|
|
80
|
-
and file_id=$3
|
|
78
|
+
await pg.query(`update crm.files set ismain=true
|
|
79
|
+
where entity_id=$1 and entity_type=$2
|
|
80
|
+
and file_id=$3
|
|
81
81
|
and (select count(*) = 0 from crm.files where entity_id=$1 and entity_type=$2 and ismain)`, [objectid, entityType, row?.file_id]);
|
|
82
82
|
}
|
|
83
83
|
return {
|
|
@@ -119,7 +119,7 @@ export default async function widgetEntitySet({ type, entityType, objectid, id,
|
|
|
119
119
|
});
|
|
120
120
|
// put w/ out file
|
|
121
121
|
if (type === "gallery" && body?.ismain && id) {
|
|
122
|
-
await pg.query(`update crm.files set ismain=false
|
|
122
|
+
await pg.query(`update crm.files set ismain=false
|
|
123
123
|
where entity_id=$1 and entity_type=$2 and file_id<>$3`, [objectid, entityType, id]);
|
|
124
124
|
}
|
|
125
125
|
return {
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" dir="" class="relative min-h-full">
|
|
3
|
-
<head>
|
|
4
|
-
<!-- scripts -->
|
|
5
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
6
|
-
</head>
|
|
7
|
-
<body class="bg-white dark:bg-neutral-900">
|
|
8
|
-
<main class="flex min-h-full">
|
|
9
|
-
|
|
10
|
-
<!-- Content -->
|
|
11
|
-
<div class="grow px-5">
|
|
12
|
-
<div class="h-full min-h-screen sm:w-[448px] flex flex-col justify-center mx-auto space-y-5">
|
|
13
|
-
|
|
14
|
-
{{#if codes}}
|
|
15
|
-
<div><h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Your recovery codes:</h1></div>
|
|
16
|
-
{{/if}}
|
|
17
|
-
{{#if recovery}}
|
|
18
|
-
<div>
|
|
19
|
-
<a href="/2factor/recovery" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
|
|
20
|
-
Send recovery code via E-mail
|
|
21
|
-
</a>
|
|
22
|
-
<h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Reset 2factor settings via recovery code:</h1>
|
|
23
|
-
</div>
|
|
24
|
-
{{/if}}
|
|
25
|
-
|
|
26
|
-
<form action="/2factor/recovery" method="post">
|
|
27
|
-
<div class="space-y-5">
|
|
28
|
-
<div>
|
|
29
|
-
<label for="hs-pro-dale" class="block mb-2 text-sm font-medium text-gray-800 dark:text-white">
|
|
30
|
-
Code
|
|
31
|
-
</label>
|
|
32
|
-
|
|
33
|
-
{{#if query.recovery}}
|
|
34
|
-
<input type="text" name="code" id="hs-pro-dale" class="py-2.5 px-3 block w-full border-gray-200 rounded-lg text-sm placeholder:text-gray-400 focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-transparent dark:border-neutral-700 dark:text-neutral-300 dark:placeholder:text-white/60 dark:focus:ring-neutral-600" placeholder="999999">
|
|
35
|
-
{{^}}
|
|
36
|
-
|
|
37
|
-
<input type="text" name="code" class="form-input hidden">
|
|
38
|
-
<div class="space-y-5">
|
|
39
|
-
<div class="my-3 flex gap-x-2" data-hs-pin-input>
|
|
40
|
-
<input id="input-1" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
41
|
-
<input id="input-2" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
42
|
-
<input id="input-3" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
43
|
-
<input id="input-4" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
44
|
-
<input id="input-5" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
45
|
-
<input id="input-6" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
{{/if}}
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
<button type="submit" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
|
|
52
|
-
Reset 2factor settings
|
|
53
|
-
</button>
|
|
54
|
-
</div>
|
|
55
|
-
</form>
|
|
56
|
-
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
<!-- End Content -->
|
|
60
|
-
</main><script>
|
|
61
|
-
const inputs = document.querySelectorAll(".input-code");
|
|
62
|
-
const formInput = document.querySelector(".form-input");
|
|
63
|
-
|
|
64
|
-
const inputTextToInput = (event) => {
|
|
65
|
-
const input = event.target;
|
|
66
|
-
const value = input.value;
|
|
67
|
-
|
|
68
|
-
if (value.length > 1) {
|
|
69
|
-
input.value = value.slice(-1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const currentIndex = Array.from(inputs).indexOf(input);
|
|
73
|
-
if (value && currentIndex < inputs.length - 1) {
|
|
74
|
-
inputs[currentIndex + 1].focus();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const combinedValue = Array.from(inputs)
|
|
78
|
-
.map((input) => input.value)
|
|
79
|
-
.join("");
|
|
80
|
-
|
|
81
|
-
formInput.value = combinedValue;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const handleBackspace = (event) => {
|
|
85
|
-
const input = event.target;
|
|
86
|
-
|
|
87
|
-
if (event.key === "Backspace" && input.value === "") {
|
|
88
|
-
const currentIndex = Array.from(inputs).indexOf(input);
|
|
89
|
-
if (currentIndex > 0) {
|
|
90
|
-
inputs[currentIndex - 1].focus();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
inputs.forEach((input) => {
|
|
96
|
-
input.addEventListener("input", inputTextToInput);
|
|
97
|
-
input.addEventListener("keydown", handleBackspace);
|
|
98
|
-
});
|
|
99
|
-
</script>
|
|
100
|
-
</body>
|
|
101
|
-
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" dir="" class="relative min-h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<!-- scripts -->
|
|
5
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
6
|
+
</head>
|
|
7
|
+
<body class="bg-white dark:bg-neutral-900">
|
|
8
|
+
<main class="flex min-h-full">
|
|
9
|
+
|
|
10
|
+
<!-- Content -->
|
|
11
|
+
<div class="grow px-5">
|
|
12
|
+
<div class="h-full min-h-screen sm:w-[448px] flex flex-col justify-center mx-auto space-y-5">
|
|
13
|
+
|
|
14
|
+
{{#if codes}}
|
|
15
|
+
<div><h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Your recovery codes:</h1></div>
|
|
16
|
+
{{/if}}
|
|
17
|
+
{{#if recovery}}
|
|
18
|
+
<div>
|
|
19
|
+
<a href="/2factor/recovery" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
|
|
20
|
+
Send recovery code via E-mail
|
|
21
|
+
</a>
|
|
22
|
+
<h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-neutral-200">Reset 2factor settings via recovery code:</h1>
|
|
23
|
+
</div>
|
|
24
|
+
{{/if}}
|
|
25
|
+
|
|
26
|
+
<form action="/2factor/recovery" method="post">
|
|
27
|
+
<div class="space-y-5">
|
|
28
|
+
<div>
|
|
29
|
+
<label for="hs-pro-dale" class="block mb-2 text-sm font-medium text-gray-800 dark:text-white">
|
|
30
|
+
Code
|
|
31
|
+
</label>
|
|
32
|
+
|
|
33
|
+
{{#if query.recovery}}
|
|
34
|
+
<input type="text" name="code" id="hs-pro-dale" class="py-2.5 px-3 block w-full border-gray-200 rounded-lg text-sm placeholder:text-gray-400 focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-transparent dark:border-neutral-700 dark:text-neutral-300 dark:placeholder:text-white/60 dark:focus:ring-neutral-600" placeholder="999999">
|
|
35
|
+
{{^}}
|
|
36
|
+
|
|
37
|
+
<input type="text" name="code" class="form-input hidden">
|
|
38
|
+
<div class="space-y-5">
|
|
39
|
+
<div class="my-3 flex gap-x-2" data-hs-pin-input>
|
|
40
|
+
<input id="input-1" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
41
|
+
<input id="input-2" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
42
|
+
<input id="input-3" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
43
|
+
<input id="input-4" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
44
|
+
<input id="input-5" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
45
|
+
<input id="input-6" type="text" class="input-code block w-[38px] h-[38px] text-center border rounded-md focus:outline-blue-600" >
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
{{/if}}
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<button type="submit" class="py-2.5 px-3 w-full inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600">
|
|
52
|
+
Reset 2factor settings
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
</form>
|
|
56
|
+
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<!-- End Content -->
|
|
60
|
+
</main><script>
|
|
61
|
+
const inputs = document.querySelectorAll(".input-code");
|
|
62
|
+
const formInput = document.querySelector(".form-input");
|
|
63
|
+
|
|
64
|
+
const inputTextToInput = (event) => {
|
|
65
|
+
const input = event.target;
|
|
66
|
+
const value = input.value;
|
|
67
|
+
|
|
68
|
+
if (value.length > 1) {
|
|
69
|
+
input.value = value.slice(-1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const currentIndex = Array.from(inputs).indexOf(input);
|
|
73
|
+
if (value && currentIndex < inputs.length - 1) {
|
|
74
|
+
inputs[currentIndex + 1].focus();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const combinedValue = Array.from(inputs)
|
|
78
|
+
.map((input) => input.value)
|
|
79
|
+
.join("");
|
|
80
|
+
|
|
81
|
+
formInput.value = combinedValue;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleBackspace = (event) => {
|
|
85
|
+
const input = event.target;
|
|
86
|
+
|
|
87
|
+
if (event.key === "Backspace" && input.value === "") {
|
|
88
|
+
const currentIndex = Array.from(inputs).indexOf(input);
|
|
89
|
+
if (currentIndex > 0) {
|
|
90
|
+
inputs[currentIndex - 1].focus();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
inputs.forEach((input) => {
|
|
96
|
+
input.addEventListener("input", inputTextToInput);
|
|
97
|
+
input.addEventListener("keydown", handleBackspace);
|
|
98
|
+
});
|
|
99
|
+
</script>
|
|
100
|
+
</body>
|
|
101
|
+
|
|
102
102
|
</html>
|