@opengis/cms 0.0.59 → 0.0.61

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.
Files changed (48) hide show
  1. package/README.md +4 -4
  2. package/dist/{ArticlesPage-BjYzvTWM.js → ArticlesPage-CFjE_cw_.js} +3 -3
  3. package/dist/{CollectionsBreadcrumb-HePNJb-d.js → CollectionsBreadcrumb-BCxeRikP.js} +1 -1
  4. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-umRzB5mY.js +53 -0
  5. package/dist/{Dashboard-CXkg_pk8.js → Dashboard-C1eGscNd.js} +132 -132
  6. package/dist/EditCollectionPage-3Q57ptN3.js +188 -0
  7. package/dist/{MenuAddPage-QTnwCoGh.js → MenuAddPage-D-p3gFgm.js} +1 -1
  8. package/dist/{MenuBody-Bi0ONVZf.js → MenuBody-rN5j4YBu.js} +2 -2
  9. package/dist/{MenuItemPage-B7Y9KFyb.js → MenuItemPage-BoJw885D.js} +3 -3
  10. package/dist/{MenuList-BLIpeqSd.js → MenuList-DFEBS0NB.js} +53 -53
  11. package/dist/{MenuPage-3W6jZ15H.js → MenuPage-BCZB_S8j.js} +1 -1
  12. package/dist/{MenuWrapper-OrOv6sOb.js → MenuWrapper-AZ_8s-zd.js} +1 -1
  13. package/dist/{MonacoEditor-ByPT8pnv.js → MonacoEditor-Db-3Jc3E.js} +1 -1
  14. package/dist/MonacoEditor.vue_vue_type_script_setup_true_lang-B1DrxmQX.js +84 -0
  15. package/dist/{UniversalTable-GBd_pStq.js → UniversalTable-CzqPG-tY.js} +80 -80
  16. package/dist/{UniversalTablePagination-Dw2hc0nc.js → UniversalTablePagination-4gL47A7I.js} +46 -46
  17. package/dist/{contentForm-Buku-lel.js → contentForm-CLStrfSg.js} +49 -52
  18. package/dist/index.js +5 -5
  19. package/dist/{vs-builder-monaco-Cw-f19gc.js → vs-builder-monaco-B3Jj0V31.js} +1 -1
  20. package/package.json +69 -69
  21. package/server/migrations/fixes.sql +5 -2
  22. package/server/migrations/site.sql +4 -3
  23. package/server/routes/cms/controllers/deleteContent.js +7 -7
  24. package/server/routes/cms/controllers/getContent.js +6 -6
  25. package/server/routes/cms/controllers/getContentBySlug.js +13 -13
  26. package/server/routes/cms/controllers/getPermissions.js +15 -15
  27. package/server/routes/cms/controllers/insertContent.js +28 -19
  28. package/server/routes/cms/controllers/setPermissions.js +49 -49
  29. package/server/routes/cms/controllers/updateContent.js +12 -47
  30. package/server/routes/cms/utils/getCollection.js +1 -1
  31. package/server/routes/cms/utils/getSingle.js +1 -1
  32. package/server/routes/cms/utils/insertContentLocalization.js +1 -1
  33. package/server/routes/cms/utils/requestTranslation.js +73 -23
  34. package/server/routes/cms/utils/updateLocalization.js +1 -6
  35. package/server/routes/cmsSpace/controllers/deleteSpace.js +1 -0
  36. package/server/routes/cmsSpace/controllers/getSpaces.js +1 -0
  37. package/server/routes/cmsSpace/controllers/insertSpace.js +1 -0
  38. package/server/routes/cmsSpace/controllers/updateSpace.js +1 -0
  39. package/server/routes/contentType/controllers/contentTypeList.js +4 -11
  40. package/server/routes/contentType/controllers/editContentType.js +7 -25
  41. package/server/routes/contentType/controllers/getContentType.js +5 -13
  42. package/server/templates/select/core.user_mentioned.sql +1 -1
  43. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-BJh-tjam.js +0 -53
  44. package/dist/EditCollectionPage-CqYHpEON.js +0 -187
  45. package/dist/MonacoEditor.vue_vue_type_script_setup_true_lang-C8cip9Ci.js +0 -84
  46. package/dist/images/logo.png +0 -0
  47. package/dist/index.html +0 -29
  48. package/dist/vite.svg +0 -1
@@ -1,16 +1,16 @@
1
- import { pgClients } from '@opengis/fastify-table/utils.js';
2
-
3
- export default async function getPermissions(req, reply) {
4
- const { pg = pgClients.client, params = {}, user = {} } = req;
5
-
6
- if (!user?.uid) {
7
- return reply.status(401).send('unauthorized');
8
- }
9
-
10
- const { rows = [] } = await pg.query(
11
- `select * from site.permissions where ${params.id ? 'user_id=$1' : 'true'}`,
12
- [params.id].filter(Boolean),
13
- );
14
-
15
- return { permissions: rows };
1
+ import { pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function getPermissions(req, reply) {
4
+ const { pg = pgClients.client, params = {}, user = {} } = req;
5
+
6
+ if (!user?.uid) {
7
+ return reply.status(401).send('unauthorized');
8
+ }
9
+
10
+ const { rows = [] } = await pg.query(
11
+ `select * from site.permissions where ${params.id ? 'user_id=$1' : 'true'}`,
12
+ [params.id].filter(Boolean),
13
+ );
14
+
15
+ return { permissions: rows };
16
16
  }
@@ -34,7 +34,7 @@ export default async function insertContent(req, reply) {
34
34
  const { type, id = body?.id } = params;
35
35
 
36
36
  if (!type) {
37
- return reply.status(400).send('not enough params: type');
37
+ return reply.status(400).send({ error: 'not enough params: type', code: 400 });
38
38
  }
39
39
 
40
40
  const arr = config.pg ? await pg.query(`select array_agg(relname)::text[] from pg_class a
@@ -52,7 +52,7 @@ export default async function insertContent(req, reply) {
52
52
  ).then(el => el.rows?.[0]?.content_type_id) : null;
53
53
 
54
54
  if (!arr.length && (ctypeId || type) !== 'pages') {
55
- return reply.status(400).send('empty schema: data');
55
+ return reply.status(400).send({ error: 'empty schema: data', code: 400 });
56
56
  }
57
57
 
58
58
  const table = arr.find(el => el === params.type);
@@ -78,7 +78,7 @@ export default async function insertContent(req, reply) {
78
78
  const ctid1 = body.content_type_id || ctid || 'pages';
79
79
 
80
80
  if (!cid) {
81
- return reply.status(404).send('contents not found');
81
+ return reply.status(404).send({ error: 'contents not found', code: 404 });
82
82
  }
83
83
 
84
84
  const columnList = columns?.map?.(el => el.name) || [];
@@ -106,19 +106,30 @@ export default async function insertContent(req, reply) {
106
106
  );
107
107
  }
108
108
 
109
+ const id1 = id || await pg.query('select next_id()').then(el => el.rows[0].next_id);
110
+
109
111
  const client = await pg.connect();
110
112
 
111
113
  try {
112
114
  await client.query('begin');
115
+ await dataInsert({
116
+ pg: client,
117
+ table: 'site.content_types',
118
+ id: id1,
119
+ data: { ...body, type: 'single', name: body.slug },
120
+ uid: user?.uid,
121
+ });
113
122
  const res = await dataInsert({
114
123
  pg: client,
115
124
  table: 'site.contents',
116
- id,
117
- data: { ...body, content_type_id: ctid1 },
125
+ id: id1,
126
+ data: { ...body, content_type_id: id1 },
118
127
  uid: user?.uid,
119
- }).then(el => el.rows?.[0] || {});
128
+ });
120
129
 
121
- if (!res?.content_id) throw new Error('insert contents error');
130
+ if (!res?.content_id) {
131
+ throw new Error('insert contents error');
132
+ }
122
133
 
123
134
  await Promise.all(keys.map(async key => dataInsert({
124
135
  pg: client,
@@ -155,7 +166,7 @@ export default async function insertContent(req, reply) {
155
166
  };
156
167
  } catch (err) {
157
168
  await client.query('rollback');
158
- return reply.status(500).send(err.toString());
169
+ return reply.status(500).send({ error: err.toString(), code: 500 });
159
170
  } finally {
160
171
  client.release();
161
172
  }
@@ -163,7 +174,7 @@ export default async function insertContent(req, reply) {
163
174
 
164
175
  // custom table
165
176
  if (!table && !dbtable) {
166
- return reply.status(400).send('invalid params: type');
177
+ return reply.status(400).send({ error: 'invalid params: type', code: 400 });
167
178
  }
168
179
 
169
180
  const client = await pg.connect();
@@ -172,7 +183,7 @@ export default async function insertContent(req, reply) {
172
183
  await client.query('begin');
173
184
 
174
185
  // const types = columns?.reduce?.((acc, curr) => ({ ...acc, [curr.name]: inputTypes[curr.type] || 'text' }), {}) || {};
175
- const { rows = [] } = await dataInsert({
186
+ const row = await dataInsert({
176
187
  pg: client,
177
188
  id,
178
189
  table: 'data.' + `"${(table || dbtable)}"`,
@@ -181,18 +192,16 @@ export default async function insertContent(req, reply) {
181
192
  uid: user?.uid,
182
193
  }).catch(err => {
183
194
  if (err.message?.includes?.('unique constraint')) {
184
- throw new Error('Порушенні унікальності: ' + err.message?.match?.(/([^']+)/g)?.[1]);
195
+ throw new Error('Порушенні унікальності: ' + err.message?.match?.(/([^']+)/g)?.[1] || err.message.split('unique constraint')[1]);
185
196
  }
186
197
  throw err;
187
- }) || {};
188
-
189
- const idRes = rows?.[0]?.id;
198
+ });
190
199
 
191
- if (!idRes) {
200
+ if (!row?.id) {
192
201
  throw new Error('content insert error');
193
202
  }
194
203
 
195
- await updateLocalization(client, idRes, body, ctid, user?.uid);
204
+ await updateLocalization(client, row.id, body, ctid, user?.uid);
196
205
 
197
206
  if (body?.tag_list?.length) {
198
207
  await Promise.all(body.tag_list.map(async tag => dataInsert({
@@ -200,7 +209,7 @@ export default async function insertContent(req, reply) {
200
209
  table: 'site.tag_data',
201
210
  data: {
202
211
  tag_id: tag,
203
- data_id: id || idRes,
212
+ data_id: id || row.id,
204
213
  },
205
214
  uid: user?.uid,
206
215
  })));
@@ -208,10 +217,10 @@ export default async function insertContent(req, reply) {
208
217
 
209
218
  await client.query('commit');
210
219
 
211
- return reply.status(200).send({ id: idRes, rows });
220
+ return reply.status(200).send({ id: row.id, rows: [row] });
212
221
  } catch (err) {
213
222
  await client.query('rollback');
214
- return reply.status(500).send(err.toString());
223
+ return reply.status(500).send({ error: err.toString(), code: 500 });
215
224
  } finally {
216
225
  client.release();
217
226
  }
@@ -1,50 +1,50 @@
1
- import { logger, pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
2
-
3
- export default async function setPermissions(req, reply) {
4
- const { pg = pgClients.client, params = {}, user = {}, body = {} } = req;
5
-
6
- if (!user?.uid) {
7
- return reply.status(401).send('unauthorized');
8
- }
9
-
10
- if (!params.id) {
11
- return reply.status(400).send('not enough params: id');
12
- }
13
-
14
- const client = await pg.connect()
15
- const result = {};
16
- try {
17
- await client.query('BEGIN');
18
-
19
- const { rowCount = 0 } = await client.query(
20
- `delete from site.permissions where user_id=$1`,
21
- [params.id].filter(Boolean),
22
- );
23
-
24
- Object.assign(result, { deleted: rowCount });
25
-
26
- if (Array.isArray(body.permissions) && body.permissions?.length) {
27
- body.permissions.forEach((el) => {
28
- Object.assign(el, { user_id: el.user_id || params.id });
29
- });
30
-
31
- await Promise.all(body.permissions.map(async (el) => dataInsert({
32
- pg: client,
33
- table: 'site.permissions',
34
- data: el,
35
- uid: user.uid,
36
- })));
37
-
38
- Object.assign(result, { inserted: body.permissions.length });
39
- }
40
-
41
- await client.query('COMMIT');
42
- return reply.status(200).send(result);
43
- } catch (err) {
44
- await client.query('ROLLBACK');
45
- logger.file('cms/permissions', { error: err.toString(), stack: err.stack });
46
- return reply.status(500).send('set permissions error');
47
- } finally {
48
- client.release();
49
- }
1
+ import { logger, pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function setPermissions(req, reply) {
4
+ const { pg = pgClients.client, params = {}, user = {}, body = {} } = req;
5
+
6
+ if (!user?.uid) {
7
+ return reply.status(401).send('unauthorized');
8
+ }
9
+
10
+ if (!params.id) {
11
+ return reply.status(400).send('not enough params: id');
12
+ }
13
+
14
+ const client = await pg.connect()
15
+ const result = {};
16
+ try {
17
+ await client.query('BEGIN');
18
+
19
+ const { rowCount = 0 } = await client.query(
20
+ `delete from site.permissions where user_id=$1`,
21
+ [params.id].filter(Boolean),
22
+ );
23
+
24
+ Object.assign(result, { deleted: rowCount });
25
+
26
+ if (Array.isArray(body.permissions) && body.permissions?.length) {
27
+ body.permissions.forEach((el) => {
28
+ Object.assign(el, { user_id: el.user_id || params.id });
29
+ });
30
+
31
+ await Promise.all(body.permissions.map(async (el) => dataInsert({
32
+ pg: client,
33
+ table: 'site.permissions',
34
+ data: el,
35
+ uid: user.uid,
36
+ })));
37
+
38
+ Object.assign(result, { inserted: body.permissions.length });
39
+ }
40
+
41
+ await client.query('COMMIT');
42
+ return reply.status(200).send(result);
43
+ } catch (err) {
44
+ await client.query('ROLLBACK');
45
+ logger.file('cms/permissions', { error: err.toString(), stack: err.stack });
46
+ return reply.status(500).send('set permissions error');
47
+ } finally {
48
+ client.release();
49
+ }
50
50
  }
@@ -34,26 +34,26 @@ export default async function updateContent(req, reply) {
34
34
  const { type, id } = params;
35
35
 
36
36
  if (!type) {
37
- return reply.status(400).send('not enough params: type');
37
+ return reply.status(400).send({ error: 'not enough params: type', code: 400 });
38
38
  }
39
39
 
40
40
  if (!id) {
41
- return reply.status(400).send('not enough params: id');
41
+ return reply.status(400).send({ error: 'not enough params: id', code: 400 });
42
42
  }
43
43
 
44
44
  if (!Object.keys(body || {}).length) {
45
- return reply.status(400).send('empty body');
45
+ return reply.status(400).send({ error: 'empty body', code: 400 });
46
46
  }
47
47
 
48
48
  // order priority - custom columns -> default for pages
49
49
  const { ctid, ctname, dbtable, columns: contentColumns = [] } = await pg.query('select content_type_id as ctid, name as ctname, table_name as dbtable, columns from site.content_types where content_type_id in (select content_type_id from site.contents where content_id=$1) or content_type_id=$2 order by content_type_id = \'pages\'', [id, type]).then(el => el.rows?.[0] || {});
50
50
 
51
- const arr = config.pg ? await pg.query(`select array_agg(relname)::text[] from pg_class a
51
+ const arr = pg ? await pg.query(`select array_agg(relname)::text[] from pg_class a
52
52
  left join pg_namespace b on a.relnamespace=b.oid
53
53
  where a.relam=2 and b.nspname='data'`).then(el => el.rows?.[0]?.array_agg || []) : [];
54
54
 
55
55
  if (!arr.length && type !== 'pages') {
56
- return reply.status(400).send('empty schema: data');
56
+ return reply.status(400).send({ error: 'empty schema: data', code: 400 });
57
57
  }
58
58
 
59
59
  const table = arr.find(el => el === params.type);
@@ -90,25 +90,16 @@ export default async function updateContent(req, reply) {
90
90
  const ctid1 = body.content_type_id || ctid;
91
91
 
92
92
  if (!cid) {
93
- return reply.status(404).send('contents not found');
93
+ return reply.status(404).send({ error: 'contents not found', code: 404 });
94
94
  }
95
95
 
96
- // ? deprecated, blocks if only default columns provided, therefore commented for now
97
- // const contentId = cid === 'pages'
98
- // ? await pg.query('select content_id from site.content_data where object_id=$1', [id]).then(el => el.rows?.[0]?.content_id)
99
- // : cid;
100
-
101
- // if (!contentId) {
102
- // return reply.status(404).send('contents not found: 2');
103
- // }
104
-
105
96
  const columnList = columns?.map?.(el => el.name) || [];
106
97
  const types = columns?.reduce?.((acc, curr) => ({ ...acc, [curr.name]: curr.type || 'text' }), {}) || {};
107
98
  const keys = Object.keys(body || {}).filter(key => columnList.includes(key) && !defaultColumns.includes(key));
108
99
 
109
100
 
110
101
  if (!Object.keys(body || {}).length) {
111
- return reply.status(400).send('invalid payload');
102
+ return reply.status(400).send({ error: 'invalid payload', code: 400 });
112
103
  }
113
104
 
114
105
  const blocks = await pg.query(`select json_object_agg(field_key,field_value) from site.content_data where content_id=$1 and field_type='reference'`, [id])
@@ -117,19 +108,9 @@ export default async function updateContent(req, reply) {
117
108
  const emptyBlock = Object.keys(body).find(key => blocks[key] && (!body[key] || typeof body[key] !== 'object' || Object.keys(body[key] || {}) === 0));
118
109
 
119
110
  if (emptyBlock) {
120
- return reply.status(400).send('access restricted: empty/invalid block ' + emptyBlock);
111
+ return reply.status(400).send({ error: 'access restricted: empty/invalid block ' + emptyBlock, code: 400 });
121
112
  }
122
113
 
123
- const single = await pg.query(
124
- `select content_id, content_type_id, slug FROM site.contents
125
- where content_id=$1`, [params.id]
126
- ).then(el => el.rows?.[0]);
127
-
128
- const customCtExists = await pg.query(
129
- `select 1 FROM site.content_types
130
- where content_type_id=$1`, [params.id]
131
- ).then(el => el.rowCount || 0);
132
-
133
114
  const client = await pg.connect();
134
115
 
135
116
  try {
@@ -137,19 +118,6 @@ export default async function updateContent(req, reply) {
137
118
 
138
119
  const res = {};
139
120
 
140
- if (single && !customCtExists && (single?.content_type_id === 'pages' || single?.content_type_id === params.id)) {
141
- // const loadTable = await getTemplate('table', 'single.default.table');
142
- // const { columns: defaultColumns } = loadTable || {};
143
- const res1 = await dataInsert({
144
- pg: client,
145
- table: 'site.content_types',
146
- id: params.id,
147
- data: { ...body, type: 'single', name: body.name === 'pages' ? (single.slug || params.id) : (body.name || body.slug) /*, columns: defaultColumns*/ },
148
- uid: user?.uid,
149
- });
150
- Object.assign(res, res1);
151
- }
152
-
153
121
  const res1 = await dataUpdate({
154
122
  pg: client,
155
123
  table: 'site.contents',
@@ -159,9 +127,6 @@ export default async function updateContent(req, reply) {
159
127
  });
160
128
  Object.assign(res, res1);
161
129
 
162
- // if (contentId) {
163
- // await client.query(`delete from site.content_data where content_id=$1`, [contentId]);
164
- // }
165
130
  const objectId = (ctname === 'pages' || ['single', 'pages'].includes(type)) && id ? id : cid;
166
131
  await client.query(`delete from site.content_data where object_id=$1`, [objectId]);
167
132
  await Promise.all(keys.map(async key => dataInsert({
@@ -204,14 +169,14 @@ export default async function updateContent(req, reply) {
204
169
  };
205
170
  } catch (err) {
206
171
  await client.query('rollback');
207
- return reply.status(500).send(err.toString());
172
+ return reply.status(500).send({ error: err.toString(), code: 500 });
208
173
  } finally {
209
174
  client.release();
210
175
  }
211
176
  }
212
177
 
213
178
  if (!table && !dbtable) {
214
- return reply.status(400).send('invalid params: type');
179
+ return reply.status(400).send({ error: 'invalid params: type', code: 400 });
215
180
  }
216
181
 
217
182
  const client = await pg.connect();
@@ -254,13 +219,13 @@ export default async function updateContent(req, reply) {
254
219
  await client.query('commit');
255
220
 
256
221
  if (!result?.id) {
257
- return reply.status(404).send('content not found');
222
+ return reply.status(404).send({ error: 'content not found', code: 404 });
258
223
  }
259
224
 
260
225
  return reply.status(200).send(result);
261
226
  } catch (err) {
262
227
  await client.query('rollback');
263
- return reply.status(500).send(err.toString());
228
+ return reply.status(500).send({ error: err.toString(), code: 500 });
264
229
  } finally {
265
230
  client.release();
266
231
  }
@@ -70,7 +70,7 @@ export default async function getCollection({
70
70
 
71
71
  const finalColumns = (defaultColumns || []).concat(columns1.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
72
72
 
73
- finalColumns.filter(el => el.default).forEach(col => {
73
+ finalColumns.filter(el => el.defaultColumn).forEach(col => {
74
74
  const { name, localization } = columns1.find(item => item.name === col.name) || {};
75
75
  if (name) {
76
76
  Object.assign(col, { localization });
@@ -26,7 +26,7 @@ export default async function getSingle({
26
26
 
27
27
  const columns = defaultColumns.concat(customColumns.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
28
28
 
29
- columns.filter(el => el.default).forEach(col => {
29
+ columns.filter(el => el.defaultColumn).forEach(col => {
30
30
  const { name, localization } = customColumns.find(item => item.name === col.name) || {};
31
31
  if (name) {
32
32
  Object.assign(col, { localization });
@@ -41,7 +41,7 @@ export default async function insertContentLocalization({ send = () => { }, tabl
41
41
  const obj = { ...row, ...localization };
42
42
 
43
43
  const entries = Object.entries(obj).filter(([key, value]) => value && schemaKeys.includes(key) && (typeof value === 'string' || (Array.isArray(value) && value?.[0] && typeof value?.[0] === 'object' && Object.keys(value).length)));
44
- // do not old localization overwrite by default
44
+ // do not overwrite old localization by default
45
45
  const filteredEntries = skip && targetLocalization ? entries.filter(([key]) => !Object.keys(targetLocalization).includes(key)) : entries;
46
46
 
47
47
  if (skip && targetLocalization && !filteredEntries.length) {
@@ -5,17 +5,26 @@ const { host = 'https://translate.softpro.ua', key = '' } = config.integrations?
5
5
  const divider = ' <divider> ';
6
6
 
7
7
  // Wrap dot notation keys in quotes
8
- export const arrayToPhrases = (arr, prefix = 'item') => {
8
+ export const arrayToPhrases = (arr, prefix = 'item', mainIdx = null) => {
9
9
  if (!arr || !arr.length) return [];
10
- return arr.flatMap((obj, idx) =>
11
- Object.entries(obj).map(([key, value]) => ({
12
- prefix,
13
- value,
14
- // phrase: `"${prefix}"."${key}".${idx}: ${value}`,
15
- key,
16
- idx
17
- }))
18
- );
10
+
11
+ return arr.flatMap((obj, idx) => {
12
+ const currentMainIdx = mainIdx ?? idx;
13
+
14
+ return Object.entries(obj).flatMap(([key, value]) => {
15
+ if (Array.isArray(value)) {
16
+ return arrayToPhrases(value, `${prefix}.${key}`, currentMainIdx);
17
+ }
18
+
19
+ return [{
20
+ prefix,
21
+ value,
22
+ key,
23
+ mainIdx: currentMainIdx,
24
+ idx,
25
+ }];
26
+ });
27
+ });
19
28
  };
20
29
 
21
30
  export default async function requestTranslation(entries, from = 'uk', to) {
@@ -26,17 +35,29 @@ export default async function requestTranslation(entries, from = 'uk', to) {
26
35
 
27
36
  // Translate each phrase independently
28
37
  const translatedPhrases = await Promise.all(
29
- phrasesWithMeta.map(async ({ key, idx, prefix, value }) => {
38
+ phrasesWithMeta.map(async ({ key, idx, prefix, value, mainIdx }) => {
30
39
  if (!value || typeof value === 'number' || value?.startsWith?.('/files/')) {
31
- return { key, idx, prefix, value, skip: true }; // fallback to original
40
+ return { key, idx, mainIdx, prefix, value, skip: true }; // fallback to original
32
41
  }
33
42
 
34
43
  try {
35
- const parts = value.startsWith('<') && value.endsWith('>')
36
- ? [...value.matchAll(/(<[^>]+>)([^<]*)(<\/[^>]+>)/g)].map(([, openTag, str, closeTag]) => ({ openTag, str, closeTag }))
37
- : [{ str: value }];
44
+ const parts =
45
+ typeof value === 'string' && value.startsWith('<') && value.endsWith('>')
46
+ ? [...value.matchAll(/(<[^>]+>)|([^<]+)/gs)].map(([, tag, str]) =>
47
+ tag
48
+ ? { tag }
49
+ : { str, tIndex: null, skip: !str.trim() || /^[—–\-.: ]+$/.test(str.trim()) ? true : false, preSpace: str.startsWith(' ') ? ' ' : '', postSpace: str.endsWith(' ') ? ' ' : '' }
50
+ )
51
+ : [{ str: value, tIndex: 0, skip: false }];
38
52
 
39
- const q = parts.map(({ str }) => str).join(divider);
53
+ // assign translation index only to fragments that should be translated
54
+ const q = [];
55
+ parts.forEach((p) => {
56
+ if (!p.tag && !p.skip) {
57
+ p.tIndex = q.length;
58
+ q.push(p.str);
59
+ }
60
+ });
40
61
 
41
62
  const resp = await fetch(`${host}/translate`, {
42
63
  method: "POST",
@@ -57,20 +78,49 @@ export default async function requestTranslation(entries, from = 'uk', to) {
57
78
  }
58
79
 
59
80
  const body = await resp.json();
60
- const resultValue = body.translatedText ? parts.map(({ openTag, str, closeTag }, i) => `${openTag || ''}${body.translatedText.split(divider)[i] || str || ''}${closeTag || ''}`).join(' ') : null;
61
- return { key, idx, prefix, value: resultValue || value };
81
+ const translated = body.translatedText || [];
82
+
83
+ const resultValue = parts
84
+ .map((p) => {
85
+ if (p.tag) return p.tag;
86
+ if (p.skip) return p.str; // keep original punctuation/whitespace
87
+ const txt = translated[p.tIndex] ?? p.str ?? '';
88
+ return `${p.preSpace || ''}${txt}${p.postSpace || ''}`; // restore spaces
89
+ })
90
+ .join('');
91
+
92
+ return { key, idx, mainIdx, prefix, value: resultValue || value };
62
93
  } catch (err) {
63
94
  console.warn('translation request failed', err.toString());
64
- return { key, idx, prefix, value, error: err.toString() }; // fallback
95
+ return { key, idx, mainIdx, prefix, value, error: err.toString() }; // fallback
65
96
  }
66
97
  })
67
98
  );
68
99
 
69
- const result = translatedPhrases.reduce((acc, { key, idx, prefix, value }) => {
70
- if (!acc[`${prefix}:${to}`]) acc[`${prefix}:${to}`] = Array.isArray(entries.find(e => e[0] === prefix)[1]) ? [] : null;
100
+ const result = translatedPhrases.reduce((acc, curr) => {
101
+ const { key, idx, prefix: prefix1, value, mainIdx } = curr;
102
+ const [prefix, subkey] = prefix1.split('.');
103
+
104
+ if (!acc[`${prefix}:${to}`]) {
105
+ acc[`${prefix}:${to}`] = Array.isArray(entries.find(e => e[0] === prefix)[1]) ? [] : null;
106
+ }
71
107
 
72
- if (Array.isArray(entries.find(e => e[0] === prefix)[1])) {
73
- if (!acc[`${prefix}:${to}`][idx]) acc[`${prefix}:${to}`][idx] = {};
108
+ if (subkey && Array.isArray(entries.find(e => e[0] === prefix)[1])) {
109
+ if (!acc[`${prefix}:${to}`][mainIdx]) {
110
+ acc[`${prefix}:${to}`][mainIdx] = {};
111
+ }
112
+ if (!acc[`${prefix}:${to}`][mainIdx][subkey]) {
113
+ acc[`${prefix}:${to}`][mainIdx][subkey] = [];
114
+ }
115
+ if (!acc[`${prefix}:${to}`][mainIdx][subkey]?.[idx]) {
116
+ acc[`${prefix}:${to}`][mainIdx][subkey][idx] = {};
117
+ }
118
+ acc[`${prefix}:${to}`][mainIdx][subkey][idx][key] = value;
119
+ // console.log(prefix, to, idx, subkey, mainIdx, key);
120
+ } else if (Array.isArray(entries.find(e => e[0] === prefix)[1])) {
121
+ if (!acc[`${prefix}:${to}`][idx]) {
122
+ acc[`${prefix}:${to}`][idx] = {};
123
+ }
74
124
  acc[`${prefix}:${to}`][idx][key] = value;
75
125
  } else {
76
126
  acc[`${prefix}:${to}`] = value;
@@ -14,16 +14,11 @@ export default async function updateLocalization(pg, id, body, contentTypeId, ui
14
14
 
15
15
  const columns = (loadTable?.columns || []).concat((contentColumns || []).filter(col => loadTable?.columns.findIndex(el => el.name === col.name) === -1));
16
16
 
17
- const locales = await pg.query('select locales from site.spaces where space_id = $1 limit 1', ['default']).then(el => el.rows?.[0]?.locales || []);
18
-
19
17
  await pg.query('delete from site.localization where object_id=$1', [id]);
20
18
 
21
- // localization disable for current space
22
- if (!locales?.length) { return null; }
23
-
24
19
  const schemaKeys = columns.filter(el => el?.name && el.localization).map(el => el.name);
25
20
 
26
- const bodyKeys = Object.keys(body || {}).filter(key => body[key] && key.includes(':') && key.split(':').pop() && locales.includes(key.split(':').pop()) && schemaKeys.includes(key.split(':').shift()));
21
+ const bodyKeys = Object.keys(body || {}).filter(key => body[key] && key.includes(':') && key.split(':').pop() && schemaKeys.includes(key.split(':').shift()));
27
22
  const obj = bodyKeys.reduce((acc, curr) => ({ ...acc, [curr]: body[curr] }), {});
28
23
 
29
24
  if (bodyKeys.length === 0) { return null; }
@@ -1,6 +1,7 @@
1
1
  import { dataDelete, pgClients } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  export default async function deleteSpace({ pg = pgClients.client, user, params, headers = {} }, reply) {
4
+ return null;
4
5
  if (!params?.id) { return reply.status(400).send('not enough params: id'); }
5
6
  if (!pg?.pk) { return reply.status(400).send('empty pg'); }
6
7
  if (!pg?.pk?.['site.spaces']) { return reply.status(400).send('table not found: site.spaces'); }
@@ -4,6 +4,7 @@ const maxLimit = 100;
4
4
  const defaultLimit = 20;
5
5
 
6
6
  export default async function getSpaces({ pg = pgClients.client, user, params = {}, query = {} }, reply) {
7
+ return null;
7
8
  if (!pg?.pk) { return reply.status(400).send('empty pg'); }
8
9
  if (!pg?.pk?.['site.spaces']) { return reply.status(400).send('table not found: site.spaces'); }
9
10
 
@@ -1,6 +1,7 @@
1
1
  import { dataInsert, pgClients } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  export default async function insertSpace({ pg = pgClients.client, user, params, headers = {}, body = {} }, reply) {
4
+ return null;
4
5
  if (!pg?.pk) { return reply.status(400).send('empty pg'); }
5
6
  if (!pg?.pk?.['site.spaces']) { return reply.status(400).send('table not found: site.spaces'); }
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { dataUpdate, pgClients } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  export default async function updateSpace({ pg = pgClients.client, user, params, headers = {}, body = {} }, reply) {
4
+ return null;
4
5
  // if (!params?.id) { return reply.status(400).send('not enough params: id'); }
5
6
  if (!pg?.pk) { return reply.status(400).send('empty pg'); }
6
7
  if (!pg?.pk?.['site.spaces']) { return reply.status(400).send('table not found: site.spaces'); }