@haiiro/docuflow 0.0.11 → 0.0.13

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 (33) hide show
  1. package/README.md +45 -9
  2. package/manifest.json +117 -16
  3. package/package.json +6 -3
  4. package/public/app/docflow-app.css +2 -0
  5. package/public/app/docflow-app.js +94 -0
  6. package/src/front-components/docflow-documents.front-component.mjs +42 -42
  7. package/src/front-components/docflow-documents.front-component.tsx +336 -128
  8. package/src/front-components/docflow-processes.front-component.mjs +35 -35
  9. package/src/front-components/docflow-processes.front-component.tsx +527 -131
  10. package/src/front-components/docflow-tasks.front-component.mjs +46 -41
  11. package/src/front-components/docflow-tasks.front-component.tsx +489 -132
  12. package/src/front-components/launch-process.front-component.mjs +75 -0
  13. package/src/front-components/launch-process.front-component.tsx +270 -0
  14. package/src/front-components/process-steps.front-component.mjs +25 -25
  15. package/src/front-components/process-steps.front-component.tsx +55 -10
  16. package/src/front-components/task-form.front-component.mjs +46 -41
  17. package/src/logic-functions/bitrix-app.logic-function.mjs +424 -198
  18. package/src/logic-functions/bitrix-app.logic-function.ts +54 -82
  19. package/src/logic-functions/bitrix-install.logic-function.mjs +0 -11
  20. package/src/logic-functions/bitrix-install.logic-function.ts +0 -3
  21. package/src/logic-functions/capture-file.logic-function.mjs +2 -1
  22. package/src/logic-functions/docflow-app-session.logic-function.mjs +60043 -0
  23. package/src/logic-functions/docflow-app-session.logic-function.ts +54 -0
  24. package/src/logic-functions/docflow-app.logic-function.mjs +60122 -0
  25. package/src/logic-functions/docflow-app.logic-function.ts +37 -0
  26. package/src/logic-functions/hand-out-form-step.logic-function.mjs +2 -1
  27. package/src/logic-functions/launch-process.logic-function.mjs +113 -29
  28. package/src/logic-functions/launch-process.logic-function.ts +83 -22
  29. package/src/logic-functions/on-workflow-run-updated.logic-function.mjs +2 -1
  30. package/src/logic-functions/publish-process.logic-function.mjs +119519 -0
  31. package/src/logic-functions/publish-process.logic-function.ts +91 -0
  32. package/src/logic-functions/save-launch-fields.logic-function.mjs +178 -10
  33. package/src/logic-functions/save-launch-fields.logic-function.ts +24 -6
@@ -1,68 +1,26 @@
1
1
  import { defineLogicFunction } from 'twenty-sdk/define';
2
- import { type RoutePayload } from 'twenty-sdk/logic-function';
2
+ import {
3
+ type LogicFunctionExecutionContext,
4
+ type RoutePayload,
5
+ } from 'twenty-sdk/logic-function';
3
6
 
4
- import { restAnd, restEq, restIsNull, restNeq, restOr } from 'src/shared/api/rest-filter.util';
5
- import { readRestList } from 'src/shared/api/rest-response.util';
6
7
  import { BitrixError, callBitrix, isBitrixConfigured } from 'src/shared/bitrix/bitrix-client';
8
+ import { type BitrixUserProfile } from 'src/shared/bitrix/bitrix-user.util';
7
9
  import { parseBitrixRequest } from 'src/shared/bitrix/bitrix.util';
8
- import { DOCFLOW_SECTION, TASK_DONE_STATUS } from 'src/shared/docflow.constants';
10
+ import { readDocflowAppSection } from 'src/shared/app/docflow-app.util';
11
+ import { findOrCreateWorkspaceMemberFromBitrix } from 'src/server/bitrix-member';
9
12
  import { proveAndLinkBitrixPortal } from 'src/server/bitrix-portal';
10
- import { renderBitrixPage, type BitrixPageTask } from 'src/server/bitrix-page';
13
+ import { renderBitrixPage } from 'src/server/bitrix-page';
14
+ import { getCrmUrl } from 'src/server/crm-url';
15
+ import { renderDocflowAppPage } from 'src/server/docflow-app-page';
11
16
  import { htmlResponse } from 'src/server/http-response';
12
- import { restClient } from 'src/server/records';
13
17
  import { BITRIX_APP_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/universal-identifiers';
14
18
 
15
- const TASKS_SHOWN_IN_PORTAL = 20;
16
-
17
- const getCrmUrl = (): string | undefined => {
18
- const value = process.env.CRM_PUBLIC_URL?.trim();
19
-
20
- return value !== undefined && /^https?:\/\//.test(value)
21
- ? value.replace(/\/$/, '')
22
- : undefined;
23
- };
24
-
25
- // The person is found by the e-mail of their Bitrix24 account; their open
26
- // docflow tasks are listed with links into the CRM.
27
- const findMyTasks = async (
28
- email: string,
29
- crmUrl: string | undefined,
30
- ): Promise<BitrixPageTask[] | undefined> => {
31
- const [member] = readRestList<{ id: string }>(
32
- await restClient('application').get('/rest/workspaceMembers', {
33
- query: { filter: restEq('userEmail', email), limit: 1 },
34
- }),
35
- ).records;
36
-
37
- if (member === undefined) {
38
- return undefined;
39
- }
40
-
41
- return readRestList<{ id: string; title?: string | null; dueAt?: string | null }>(
42
- await restClient('application').get('/rest/tasks', {
43
- query: {
44
- filter: restAnd(
45
- restEq('smbSection', DOCFLOW_SECTION),
46
- restEq('assigneeId', member.id),
47
- restOr(restNeq('status', TASK_DONE_STATUS), restIsNull('status')),
48
- ),
49
- order_by: 'dueAt[AscNullsLast]',
50
- limit: TASKS_SHOWN_IN_PORTAL,
51
- },
52
- }),
53
- ).records.map((task) => ({
54
- title: task.title?.trim() || 'Без названия',
55
- url: crmUrl !== undefined ? `${crmUrl}/object/task/${task.id}` : undefined,
56
- dueAt:
57
- typeof task.dueAt === 'string'
58
- ? new Date(task.dueAt).toLocaleDateString('ru-RU')
59
- : undefined,
60
- }));
61
- };
62
-
63
19
  // "Путь вашего обработчика" of the local Bitrix24 application: the page a
64
- // person sees when they open the app in the portal.
65
- const handler = async (event: RoutePayload) => {
20
+ // person sees when they open the app in the portal. After the portal and the
21
+ // person are checked it is the standalone docflow page; it signs in to the
22
+ // CRM itself (the portal frame gets no CRM cookie).
23
+ const handler = async (event: RoutePayload, context: LogicFunctionExecutionContext) => {
66
24
  const crmUrl = getCrmUrl();
67
25
 
68
26
  if (!isBitrixConfigured()) {
@@ -72,7 +30,6 @@ const handler = async (event: RoutePayload) => {
72
30
  title: 'Документооборот',
73
31
  message: 'В настройках приложения в CRM не указаны BITRIX_CLIENT_ID и BITRIX_CLIENT_SECRET.',
74
32
  crmUrl,
75
- tasks: [],
76
33
  isInstall: false,
77
34
  }),
78
35
  );
@@ -86,43 +43,58 @@ const handler = async (event: RoutePayload) => {
86
43
 
87
44
  try {
88
45
  const portal = await proveAndLinkBitrixPortal(request);
89
- const user = await callBitrix<{ EMAIL?: string }>({
46
+ const user = await callBitrix<BitrixUserProfile>({
90
47
  restEndpoint: portal.restEndpoint,
91
48
  accessToken: request.authId ?? portal.accessToken,
92
49
  method: 'user.current',
93
50
  params: {},
94
51
  });
95
- const tasks =
96
- typeof user.EMAIL === 'string' && user.EMAIL.length > 0
97
- ? await findMyTasks(user.EMAIL, crmUrl)
98
- : undefined;
52
+ const member = await findOrCreateWorkspaceMemberFromBitrix(user);
99
53
 
100
- return htmlResponse(
101
- 200,
102
- renderBitrixPage({
103
- title: 'Документооборот',
104
- message:
105
- tasks === undefined
106
- ? 'В CRM нет сотрудника с почтой вашего профиля Битрикс24.'
107
- : tasks.length === 0
108
- ? 'Открытых заданий нет.'
109
- : 'Ваши открытые задания:',
110
- crmUrl,
111
- tasks: tasks ?? [],
112
- isInstall: false,
113
- }),
114
- );
54
+ if (member === undefined) {
55
+ return htmlResponse(
56
+ 200,
57
+ renderBitrixPage({
58
+ title: 'Документооборот',
59
+ message: 'В профиле Битрикс24 нет почты — без неё нельзя найти или создать сотрудника в CRM.',
60
+ crmUrl,
61
+ isInstall: false,
62
+ }),
63
+ );
64
+ }
65
+
66
+ return renderDocflowAppPage({
67
+ workspaceId: context.workspaceId,
68
+ config: {
69
+ mode: 'bitrix',
70
+ crmUrl: crmUrl ?? '',
71
+ section: readDocflowAppSection(event.queryStringParameters.section),
72
+ expectedWorkspaceMemberId: member.id,
73
+ portalDomain: portal.portalDomain,
74
+ },
75
+ });
115
76
  } catch (error) {
77
+ if (error instanceof BitrixError) {
78
+ return htmlResponse(
79
+ 403,
80
+ renderBitrixPage({
81
+ title: 'Документооборот',
82
+ message: `Запрос портала не подтверждён: ${error.message}`,
83
+ crmUrl,
84
+ isInstall: false,
85
+ }),
86
+ );
87
+ }
88
+
116
89
  return htmlResponse(
117
- 403,
90
+ 200,
118
91
  renderBitrixPage({
119
92
  title: 'Документооборот',
120
93
  message:
121
- error instanceof BitrixError
122
- ? `Запрос портала не подтверждён: ${error.message}`
123
- : 'Запрос портала не подтверждён.',
94
+ error instanceof Error
95
+ ? error.message
96
+ : 'Не удалось открыть документооборот для вашего профиля Битрикс24.',
124
97
  crmUrl,
125
- tasks: [],
126
98
  isInstall: false,
127
99
  }),
128
100
  );
@@ -119538,7 +119538,6 @@ var renderBitrixPage = ({
119538
119538
  title,
119539
119539
  message,
119540
119540
  crmUrl,
119541
- tasks,
119542
119541
  isInstall
119543
119542
  }) => `<!doctype html>
119544
119543
  <html lang="ru">
@@ -119552,19 +119551,12 @@ var renderBitrixPage = ({
119552
119551
  h1 { font-size: 20px; margin: 0 0 8px; }
119553
119552
  p { color: #666; margin: 0 0 16px; }
119554
119553
  a.button { display: inline-block; padding: 8px 16px; border-radius: 8px; background: #1961ed; color: #fff; text-decoration: none; }
119555
- ul { list-style: none; padding: 0; margin: 16px 0 0; }
119556
- li { padding: 8px 0; border-top: 1px solid #eee; display: flex; justify-content: space-between; gap: 16px; }
119557
- li a { color: #1961ed; text-decoration: none; }
119558
- .due { color: #999; white-space: nowrap; }
119559
119554
  </style>
119560
119555
  </head>
119561
119556
  <body>
119562
119557
  <h1>${escapeHtml(title)}</h1>
119563
119558
  <p>${escapeHtml(message)}</p>
119564
119559
  ${crmUrl !== void 0 ? `<a class="button" href="${escapeHtml(crmUrl)}" target="_blank" rel="noopener">\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442</a>` : ""}
119565
- ${tasks.length > 0 ? `<ul>${tasks.map(
119566
- (task) => `<li>${task.url !== void 0 ? `<a href="${escapeHtml(task.url)}" target="_blank" rel="noopener">${escapeHtml(task.title)}</a>` : escapeHtml(task.title)}<span class="due">${escapeHtml(task.dueAt ?? "")}</span></li>`
119567
- ).join("")}</ul>` : ""}
119568
119560
  ${isInstall ? "<script>if (window.BX24) { BX24.init(function () { BX24.installFinish(); }); }</script>" : ""}
119569
119561
  </body>
119570
119562
  </html>`;
@@ -119586,7 +119578,6 @@ var handler = async (event) => {
119586
119578
  renderBitrixPage({
119587
119579
  title: "\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442",
119588
119580
  message: "\u0412 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0432 CRM \u043D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u044B BITRIX_CLIENT_ID \u0438 BITRIX_CLIENT_SECRET.",
119589
- tasks: [],
119590
119581
  isInstall: false
119591
119582
  })
119592
119583
  );
@@ -119613,7 +119604,6 @@ var handler = async (event) => {
119613
119604
  renderBitrixPage({
119614
119605
  title: "\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D",
119615
119606
  message: `\u041F\u043E\u0440\u0442\u0430\u043B ${portal.portalDomain} \u0441\u0432\u044F\u0437\u0430\u043D \u0441 CRM.`,
119616
- tasks: [],
119617
119607
  isInstall: true
119618
119608
  })
119619
119609
  );
@@ -119623,7 +119613,6 @@ var handler = async (event) => {
119623
119613
  renderBitrixPage({
119624
119614
  title: "\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442",
119625
119615
  message: error instanceof BitrixError ? `\u041F\u043E\u0440\u0442\u0430\u043B \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0451\u043D: ${error.message}` : "\u041F\u043E\u0440\u0442\u0430\u043B \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0451\u043D.",
119626
- tasks: [],
119627
119616
  isInstall: false
119628
119617
  })
119629
119618
  );
@@ -17,7 +17,6 @@ const handler = async (event: RoutePayload) => {
17
17
  renderBitrixPage({
18
18
  title: 'Документооборот',
19
19
  message: 'В настройках приложения в CRM не указаны BITRIX_CLIENT_ID и BITRIX_CLIENT_SECRET.',
20
- tasks: [],
21
20
  isInstall: false,
22
21
  }),
23
22
  );
@@ -47,7 +46,6 @@ const handler = async (event: RoutePayload) => {
47
46
  renderBitrixPage({
48
47
  title: 'Документооборот подключён',
49
48
  message: `Портал ${portal.portalDomain} связан с CRM.`,
50
- tasks: [],
51
49
  isInstall: true,
52
50
  }),
53
51
  );
@@ -60,7 +58,6 @@ const handler = async (event: RoutePayload) => {
60
58
  error instanceof BitrixError
61
59
  ? `Портал не подтверждён: ${error.message}`
62
60
  : 'Портал не подтверждён.',
63
- tasks: [],
64
61
  isInstall: false,
65
62
  }),
66
63
  );
@@ -42,7 +42,8 @@ var readRestList = (response) => {
42
42
  const value = readDataValue(response);
43
43
  return {
44
44
  records: Array.isArray(value) ? value : [],
45
- pageInfo: response?.pageInfo ?? {}
45
+ pageInfo: response?.pageInfo ?? {},
46
+ totalCount: response?.totalCount
46
47
  };
47
48
  };
48
49
  var readRestRecord = (response) => {