shutro-erp 0.3.2 → 0.3.4

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 (156) hide show
  1. package/dist/account_portal/.kosh/bindings.js +1 -1
  2. package/dist/account_portal/.kosh/schema.d.ts +156 -1
  3. package/dist/accounting/.kosh/bindings.js +1 -1
  4. package/dist/accounting/.kosh/schema.d.ts +156 -1
  5. package/dist/assistant/index.js +1 -1
  6. package/dist/contacts/.kosh/bindings.js +1 -1
  7. package/dist/contacts/.kosh/schema.d.ts +156 -1
  8. package/dist/integrations/index.js +12 -0
  9. package/dist/integrations/web/OAuthAppsPage.d.ts +9 -0
  10. package/dist/integrations/web/OAuthAppsPage.js +79 -0
  11. package/dist/integrations/web/index.d.ts +1 -0
  12. package/dist/integrations/web/index.js +3 -0
  13. package/dist/integrations/web/messages.js +58 -0
  14. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  15. package/dist/l10n_bd/.kosh/schema.d.ts +156 -1
  16. package/dist/l10n_in/.kosh/bindings.js +1 -1
  17. package/dist/l10n_in/.kosh/schema.d.ts +156 -4
  18. package/dist/l10n_in/module.d.ts +0 -62
  19. package/dist/l10n_in/module.js +2 -4
  20. package/dist/messaging/web/index.js +1 -1
  21. package/dist/notify/web/index.js +1 -1
  22. package/dist/payment/.kosh/bindings.js +1 -1
  23. package/dist/payment/.kosh/schema.d.ts +157 -2
  24. package/dist/payment/bkash.d.ts +28 -0
  25. package/dist/payment/bkash.js +169 -0
  26. package/dist/payment/index.d.ts +4 -0
  27. package/dist/payment/index.js +80 -24
  28. package/dist/payment/module.d.ts +7 -4
  29. package/dist/payment/module.js +3 -0
  30. package/dist/payment/money.d.ts +7 -0
  31. package/dist/payment/money.js +37 -0
  32. package/dist/payment/providers.d.ts +13 -2
  33. package/dist/payment/razorpay.d.ts +12 -0
  34. package/dist/payment/razorpay.js +149 -0
  35. package/dist/payment/sslcommerz.js +1 -1
  36. package/dist/payment/stripe.d.ts +15 -0
  37. package/dist/payment/stripe.js +182 -0
  38. package/dist/permissions/admin.d.ts +3 -0
  39. package/dist/permissions/admin.js +108 -10
  40. package/dist/permissions/index.d.ts +1 -0
  41. package/dist/permissions/index.js +3 -0
  42. package/dist/permissions/roles.d.ts +2 -0
  43. package/dist/permissions/roles.js +236 -0
  44. package/dist/portal/.kosh/bindings.js +1 -1
  45. package/dist/portal/.kosh/schema.d.ts +156 -1
  46. package/dist/product/.kosh/bindings.js +1 -1
  47. package/dist/product/.kosh/schema.d.ts +156 -1
  48. package/dist/project/.kosh/bindings.js +1 -1
  49. package/dist/project/.kosh/schema.d.ts +156 -1
  50. package/dist/project_portal/.kosh/bindings.js +1 -1
  51. package/dist/project_portal/.kosh/schema.d.ts +156 -1
  52. package/dist/purchase/.kosh/bindings.js +1 -1
  53. package/dist/purchase/.kosh/schema.d.ts +156 -1
  54. package/dist/purchase_account/.kosh/bindings.js +1 -1
  55. package/dist/purchase_account/.kosh/schema.d.ts +156 -1
  56. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  57. package/dist/purchase_portal/.kosh/schema.d.ts +156 -1
  58. package/dist/sale_account/.kosh/bindings.js +1 -1
  59. package/dist/sale_account/.kosh/schema.d.ts +156 -1
  60. package/dist/sale_portal/.kosh/bindings.js +1 -1
  61. package/dist/sale_portal/.kosh/schema.d.ts +156 -1
  62. package/dist/sale_project/.kosh/bindings.js +1 -1
  63. package/dist/sale_project/.kosh/schema.d.ts +156 -1
  64. package/dist/sales/.kosh/bindings.js +1 -1
  65. package/dist/sales/.kosh/schema.d.ts +156 -1
  66. package/dist/server/auth/base.js +98 -0
  67. package/dist/server/auth/oauth.d.ts +21 -0
  68. package/dist/server/auth/oauth.js +579 -0
  69. package/dist/server/auth/routes.d.ts +3 -0
  70. package/dist/server/auth/routes.js +29 -0
  71. package/dist/server/auth/service.d.ts +56 -0
  72. package/dist/server/auth/service.js +185 -3
  73. package/dist/server/identity.d.ts +4 -0
  74. package/dist/server/images.d.ts +12 -0
  75. package/dist/server/images.js +139 -0
  76. package/dist/server/index.d.ts +2 -1
  77. package/dist/server/index.js +1 -0
  78. package/dist/server/server.d.ts +4 -0
  79. package/dist/server/server.js +20 -4
  80. package/dist/settings/company.d.ts +34 -0
  81. package/dist/settings/company.js +189 -0
  82. package/dist/settings/index.d.ts +10 -10
  83. package/dist/settings/index.js +159 -30
  84. package/dist/settings/people.d.ts +16 -0
  85. package/dist/settings/people.js +507 -0
  86. package/dist/settings/security.d.ts +13 -0
  87. package/dist/settings/security.js +158 -0
  88. package/dist/settings/web/AuditPage.d.ts +8 -0
  89. package/dist/settings/web/AuditPage.js +74 -0
  90. package/dist/settings/web/CompaniesPage.d.ts +8 -0
  91. package/dist/settings/web/CompaniesPage.js +52 -0
  92. package/dist/settings/web/CompanyPage.d.ts +9 -0
  93. package/dist/settings/web/CompanyPage.js +105 -0
  94. package/dist/settings/web/ConnectedAppsPage.d.ts +7 -0
  95. package/dist/settings/web/ConnectedAppsPage.js +33 -0
  96. package/dist/settings/web/OverviewPage.d.ts +9 -0
  97. package/dist/settings/web/OverviewPage.js +77 -0
  98. package/dist/settings/web/PeoplePage.d.ts +9 -0
  99. package/dist/settings/web/PeoplePage.js +154 -0
  100. package/dist/settings/web/ProfilePage.d.ts +14 -0
  101. package/dist/settings/web/ProfilePage.js +196 -0
  102. package/dist/settings/web/RolesEditor.d.ts +35 -0
  103. package/dist/settings/web/RolesEditor.js +49 -0
  104. package/dist/settings/web/RolesPage.d.ts +9 -0
  105. package/dist/settings/web/RolesPage.js +296 -0
  106. package/dist/settings/web/SecurityPage.d.ts +8 -0
  107. package/dist/settings/web/SecurityPage.js +81 -0
  108. package/dist/settings/web/index.d.ts +10 -0
  109. package/dist/settings/web/index.js +31 -1
  110. package/dist/settings/web/messages.js +594 -0
  111. package/dist/ui/api.d.ts +8 -0
  112. package/dist/ui/app.d.ts +5 -0
  113. package/dist/ui/app.js +143 -33
  114. package/dist/ui/components/AppIcon.js +11 -1
  115. package/dist/ui/components/Avatar.d.ts +21 -0
  116. package/dist/ui/components/Avatar.js +71 -0
  117. package/dist/ui/components/primitives.d.ts +9 -1
  118. package/dist/ui/components/primitives.js +12 -4
  119. package/dist/ui/context.d.ts +10 -1
  120. package/dist/ui/context.js +60 -2
  121. package/dist/ui/i18n/messages.d.ts +76 -0
  122. package/dist/ui/i18n/messages.js +152 -0
  123. package/dist/ui/index.d.ts +3 -1
  124. package/dist/ui/index.js +3 -1
  125. package/dist/ui/lib/accent.d.ts +13 -0
  126. package/dist/ui/lib/accent.js +36 -0
  127. package/dist/ui/registry.d.ts +7 -1
  128. package/dist/ui/registry.js +10 -1
  129. package/dist/ui/search/ViewTabs.d.ts +14 -0
  130. package/dist/ui/search/ViewTabs.js +42 -0
  131. package/dist/ui/shell/Activate.d.ts +6 -0
  132. package/dist/ui/shell/Activate.js +39 -0
  133. package/dist/ui/shell/CommandPalette.d.ts +7 -1
  134. package/dist/ui/shell/CommandPalette.js +16 -2
  135. package/dist/ui/shell/Consent.d.ts +5 -0
  136. package/dist/ui/shell/Consent.js +35 -0
  137. package/dist/ui/shell/Home.js +85 -16
  138. package/dist/ui/shell/Sidebar.d.ts +27 -0
  139. package/dist/ui/shell/Sidebar.js +77 -0
  140. package/dist/ui/shell/TwoFactorSetup.d.ts +5 -0
  141. package/dist/ui/shell/TwoFactorSetup.js +32 -0
  142. package/dist/ui/shell/shortcuts.d.ts +3 -0
  143. package/dist/ui/shell/shortcuts.js +13 -4
  144. package/dist/ui/styles.css +70 -28
  145. package/dist/ui/views/DateField.d.ts +10 -0
  146. package/dist/ui/views/DateField.js +129 -0
  147. package/dist/ui/views/FormView.js +161 -21
  148. package/dist/ui/views/ListView.d.ts +2 -0
  149. package/dist/ui/views/ListView.js +57 -25
  150. package/dist/ui/views/StatusChip.d.ts +10 -0
  151. package/dist/ui/views/StatusChip.js +22 -0
  152. package/dist/ui/views/widgets.d.ts +3 -0
  153. package/dist/ui/views/widgets.js +26 -8
  154. package/dist/views/types.d.ts +7 -0
  155. package/dist/views/validate.js +4 -0
  156. package/package.json +2 -2
@@ -4,6 +4,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  * formatter for the user's language and time zone, and the theme.
5
5
  */
6
6
  import { createContext, useContext, useEffect, useMemo, useState, } from 'react';
7
+ import { accentVariables } from './lib/accent.js';
7
8
  import { createFormatter } from './lib/format.js';
8
9
  const Context = createContext(null);
9
10
  /** The renderer context. @throws outside {@link KoshProvider} */
@@ -14,6 +15,45 @@ export function useKosh() {
14
15
  return value;
15
16
  }
16
17
  const THEME_KEY = 'kosh.theme';
18
+ const DENSITY_KEY = 'kosh.density';
19
+ function readDensity() {
20
+ try {
21
+ return JSON.parse(localStorage.getItem(DENSITY_KEY) ?? 'null') === 'compact'
22
+ ? 'compact'
23
+ : 'comfortable';
24
+ }
25
+ catch {
26
+ return 'comfortable';
27
+ }
28
+ }
29
+ /** Applies the density to `<html>` (`data-density`), remembered per browser. */
30
+ function useDensity() {
31
+ const [density, setDensity] = useState(readDensity);
32
+ useEffect(() => {
33
+ document.documentElement.dataset['density'] = density;
34
+ try {
35
+ localStorage.setItem(DENSITY_KEY, JSON.stringify(density));
36
+ }
37
+ catch {
38
+ // storage unavailable: the choice lasts for this page only
39
+ }
40
+ }, [density]);
41
+ return [density, setDensity];
42
+ }
43
+ /** Sets the company's brand colour as the accent (RFC 0018), or the default when it has none. */
44
+ function useAccent(accent) {
45
+ useEffect(() => {
46
+ const root = document.documentElement.style;
47
+ const variables = accentVariables(accent);
48
+ for (const key of ['--accent-h', '--accent-c']) {
49
+ const value = variables[key];
50
+ if (value === undefined)
51
+ root.removeProperty(key);
52
+ else
53
+ root.setProperty(key, value);
54
+ }
55
+ }, [accent]);
56
+ }
17
57
  function readTheme() {
18
58
  try {
19
59
  const stored = localStorage.getItem(THEME_KEY);
@@ -47,8 +87,10 @@ function useTheme() {
47
87
  }, [theme]);
48
88
  return [theme, setTheme];
49
89
  }
50
- export function KoshProvider({ api, registry, uid, companyId, lang, tz, grouping, realtime, offline, children, }) {
90
+ export function KoshProvider({ api, registry, uid, companyId, lang, tz, grouping, accent, realtime, offline, switchCompany, children, }) {
51
91
  const [theme, setTheme] = useTheme();
92
+ const [density, setDensity] = useDensity();
93
+ useAccent(accent);
52
94
  const format = useMemo(() => createFormatter(lang, tz, grouping === undefined ? {} : { grouping }), [lang, tz, grouping]);
53
95
  const value = useMemo(() => ({
54
96
  api,
@@ -58,9 +100,25 @@ export function KoshProvider({ api, registry, uid, companyId, lang, tz, grouping
58
100
  format,
59
101
  theme,
60
102
  setTheme,
103
+ density,
104
+ setDensity,
61
105
  ...(realtime === undefined ? {} : { realtime }),
62
106
  ...(offline === undefined ? {} : { offline }),
63
- }), [api, registry, uid, companyId, format, theme, setTheme, realtime, offline]);
107
+ ...(switchCompany === undefined ? {} : { switchCompany }),
108
+ }), [
109
+ api,
110
+ registry,
111
+ uid,
112
+ companyId,
113
+ format,
114
+ theme,
115
+ setTheme,
116
+ density,
117
+ setDensity,
118
+ realtime,
119
+ offline,
120
+ switchCompany,
121
+ ]);
64
122
  return _jsx(Context.Provider, { value: value, children: children });
65
123
  }
66
124
  /** Theme switching without a signed-in user (the sign-in page). */
@@ -12,6 +12,14 @@ export declare const en: {
12
12
  unknownPage: string;
13
13
  };
14
14
  auth: {
15
+ activateTitle: string;
16
+ activateIntro: string;
17
+ activateNoToken: string;
18
+ newPassword: string;
19
+ repeatPassword: string;
20
+ passwordRule: string;
21
+ mismatch: string;
22
+ activate: string;
15
23
  signIn: string;
16
24
  login: string;
17
25
  password: string;
@@ -21,11 +29,32 @@ export declare const en: {
21
29
  signOut: string;
22
30
  invalid: string;
23
31
  adminNeeds2fa: string;
32
+ setupTitle: string;
33
+ setupIntro: string;
34
+ setupStart: string;
35
+ setupStep1: string;
36
+ setupOpenApp: string;
37
+ setupStep2: string;
38
+ setupStep3: string;
39
+ setupTurnOn: string;
40
+ setupCodes: string;
41
+ setupContinue: string;
24
42
  };
25
43
  shell: {
26
44
  apps: string;
27
45
  menu: string;
28
46
  theme: string;
47
+ density: string;
48
+ home: string;
49
+ appNamed: string;
50
+ switchCompany: string;
51
+ sidebar: string;
52
+ collapseSidebar: string;
53
+ expandSidebar: string;
54
+ openMenu: string;
55
+ closeMenu: string;
56
+ menuHint: string;
57
+ location: string;
29
58
  light: string;
30
59
  dark: string;
31
60
  system: string;
@@ -62,6 +91,20 @@ export declare const en: {
62
91
  exportFailed: string;
63
92
  columns: string;
64
93
  density: string;
94
+ statusOf: string;
95
+ nextState: string;
96
+ recordActions: string;
97
+ summary: string;
98
+ details: string;
99
+ recordPanel: string;
100
+ showPanel: string;
101
+ hidePanel: string;
102
+ previousMonth: string;
103
+ nextMonth: string;
104
+ pickDate: string;
105
+ datePlaceholder: string;
106
+ today: string;
107
+ clear: string;
65
108
  proposed: string;
66
109
  design: {
67
110
  addField: string;
@@ -70,6 +113,7 @@ export declare const en: {
70
113
  compact: string;
71
114
  comfortable: string;
72
115
  emptyTitle: string;
116
+ emptyNamed: string;
73
117
  emptyHint: string;
74
118
  emptyFiltered: string;
75
119
  moved: string;
@@ -191,12 +235,38 @@ export declare const en: {
191
235
  done: string;
192
236
  recent: string;
193
237
  apps: string;
238
+ today: string;
239
+ approve: string;
240
+ review: string;
241
+ dueToday: string;
242
+ mentioned: string;
243
+ saved: string;
194
244
  };
195
245
  toast: {
196
246
  region: string;
197
247
  close: string;
198
248
  };
249
+ image: {
250
+ choosePhoto: string;
251
+ chooseLogo: string;
252
+ remove: string;
253
+ hint: string;
254
+ unreadable: string;
255
+ };
256
+ oauth: {
257
+ title: string;
258
+ as: string;
259
+ may: string;
260
+ limits: string;
261
+ back: string;
262
+ homepage: string;
263
+ already: string;
264
+ allow: string;
265
+ cancel: string;
266
+ };
199
267
  palette: {
268
+ switchCompany: string;
269
+ profile: string;
200
270
  button: string;
201
271
  title: string;
202
272
  description: string;
@@ -220,6 +290,7 @@ export declare const en: {
220
290
  shortcuts: {
221
291
  title: string;
222
292
  palette: string;
293
+ sidebar: string;
223
294
  search: string;
224
295
  new: string;
225
296
  views: string;
@@ -246,6 +317,11 @@ export declare const en: {
246
317
  custom: string;
247
318
  remove: string;
248
319
  or: string;
320
+ savedViews: string;
321
+ allRecords: string;
322
+ moreViews_one: string;
323
+ moreViews_other: string;
324
+ shared: string;
249
325
  };
250
326
  };
251
327
  type Messages = typeof en;
@@ -12,6 +12,14 @@ export const en = {
12
12
  unknownPage: 'The page "{{page}}" is not part of this app. Register it with registry.registerPage.',
13
13
  },
14
14
  auth: {
15
+ activateTitle: 'Choose your password',
16
+ activateIntro: 'You were invited to Shutro ERP. Choose a password to start.',
17
+ activateNoToken: 'This link is incomplete: open the link from your invitation.',
18
+ newPassword: 'Password',
19
+ repeatPassword: 'Repeat the password',
20
+ passwordRule: 'At least 12 characters.',
21
+ mismatch: 'The passwords are not the same.',
22
+ activate: 'Save and sign in',
15
23
  signIn: 'Sign in',
16
24
  login: 'Login',
17
25
  password: 'Password',
@@ -21,11 +29,32 @@ export const en = {
21
29
  signOut: 'Sign out',
22
30
  invalid: 'The login or password is wrong.',
23
31
  adminNeeds2fa: 'Administrator rights apply after you verify a second factor.',
32
+ setupTitle: 'Set up two-factor sign-in',
33
+ setupIntro: 'This company requires a second step when you sign in: a code from an authenticator app on your phone. Set it up to continue.',
34
+ setupStart: 'Set it up',
35
+ setupStep1: 'Add this account to your authenticator app.',
36
+ setupOpenApp: 'Open in the app',
37
+ setupStep2: 'Or type this key:',
38
+ setupStep3: 'Enter the 6-digit code it shows.',
39
+ setupTurnOn: 'Turn on and continue',
40
+ setupCodes: 'Keep these recovery codes somewhere safe: each signs you in once if you lose your phone.',
41
+ setupContinue: 'Continue',
24
42
  },
25
43
  shell: {
26
44
  apps: 'Apps',
27
45
  menu: 'Main menu',
28
46
  theme: 'Theme',
47
+ density: 'Density',
48
+ home: 'Home',
49
+ appNamed: '{{app}} app',
50
+ switchCompany: 'Company: {{name}} (switch)',
51
+ sidebar: 'Sidebar',
52
+ collapseSidebar: 'Collapse the sidebar',
53
+ expandSidebar: 'Expand the sidebar',
54
+ openMenu: 'Open the menu',
55
+ closeMenu: 'Close the menu',
56
+ menuHint: 'Apps and their sections',
57
+ location: 'You are here',
29
58
  light: 'Light',
30
59
  dark: 'Dark',
31
60
  system: 'System',
@@ -62,11 +91,26 @@ export const en = {
62
91
  exportFailed: 'The export failed: {{reason}}',
63
92
  columns: 'Columns',
64
93
  density: 'Density',
94
+ statusOf: '{{field}}: {{state}}',
95
+ nextState: 'Next',
96
+ recordActions: 'Record actions',
97
+ summary: 'Summary',
98
+ details: 'Details',
99
+ recordPanel: 'About this record',
100
+ showPanel: 'Show the panel',
101
+ hidePanel: 'Hide the panel',
102
+ previousMonth: 'Previous month',
103
+ nextMonth: 'Next month',
104
+ pickDate: 'Pick a date',
105
+ datePlaceholder: 'Date',
106
+ today: 'Today',
107
+ clear: 'Clear',
65
108
  proposed: 'Proposed',
66
109
  design: { addField: 'Add field', addColumn: 'Add column' },
67
110
  compact: 'Compact',
68
111
  comfortable: 'Comfortable',
69
112
  emptyTitle: 'Nothing here yet',
113
+ emptyNamed: 'No {{name}} yet',
70
114
  emptyHint: 'Records matching the current search will appear here.',
71
115
  emptyFiltered: 'No record matches the current search.',
72
116
  moved: '{{name}} moved to {{column}}',
@@ -188,9 +232,35 @@ export const en = {
188
232
  done: 'Mark done',
189
233
  recent: 'Recently opened',
190
234
  apps: 'Apps',
235
+ today: 'Today, {{date}}',
236
+ approve: 'Approve {{record}}',
237
+ review: 'Review',
238
+ dueToday: 'due today',
239
+ mentioned: '{{actor}} mentioned you on {{record}}',
240
+ saved: 'Your saved views',
191
241
  },
192
242
  toast: { region: 'Notifications', close: 'Close' },
243
+ image: {
244
+ choosePhoto: 'Change photo',
245
+ chooseLogo: 'Change logo',
246
+ remove: 'Remove',
247
+ hint: 'PNG, JPEG or WebP. It is made small (256 px) before it is sent.',
248
+ unreadable: 'This file is not an image the browser can read.',
249
+ },
250
+ oauth: {
251
+ title: '{{app}} wants to use your account',
252
+ as: 'Signed in as {{name}} ({{login}}).',
253
+ may: 'It will be able to:',
254
+ limits: 'Never more than you may do yourself. You can take this back at any time in Connected apps.',
255
+ back: 'You will go back to {{host}}.',
256
+ homepage: 'About the app',
257
+ already: 'You allowed this before.',
258
+ allow: 'Allow',
259
+ cancel: 'Cancel',
260
+ },
193
261
  palette: {
262
+ switchCompany: 'Switch to {{name}}',
263
+ profile: 'Your profile',
194
264
  button: 'Search or jump to…',
195
265
  title: 'Command palette',
196
266
  description: 'Search records, menus and commands. Use the arrow keys and Enter.',
@@ -209,6 +279,7 @@ export const en = {
209
279
  shortcuts: {
210
280
  title: 'Keyboard shortcuts',
211
281
  palette: 'Search or jump anywhere',
282
+ sidebar: 'Collapse or expand the sidebar',
212
283
  search: 'Focus the search box',
213
284
  new: 'New record',
214
285
  views: 'Switch view',
@@ -235,6 +306,11 @@ export const en = {
235
306
  custom: 'Custom filter',
236
307
  remove: 'Remove {{facet}}',
237
308
  or: 'or',
309
+ savedViews: 'Saved views',
310
+ allRecords: 'All',
311
+ moreViews_one: '{{count}} more',
312
+ moreViews_other: '{{count}} more',
313
+ shared: 'Shared',
238
314
  },
239
315
  };
240
316
  export const bn = {
@@ -247,6 +323,14 @@ export const bn = {
247
323
  unknownPage: '"{{page}}" পৃষ্ঠাটি এই অ্যাপে নেই। registry.registerPage দিয়ে নিবন্ধন করুন।',
248
324
  },
249
325
  auth: {
326
+ activateTitle: 'আপনার পাসওয়ার্ড বেছে নিন',
327
+ activateIntro: 'আপনাকে Shutro ERP-এ আমন্ত্রণ জানানো হয়েছে। শুরু করতে একটি পাসওয়ার্ড বেছে নিন।',
328
+ activateNoToken: 'লিংকটি অসম্পূর্ণ: আমন্ত্রণের লিংকটি খুলুন।',
329
+ newPassword: 'পাসওয়ার্ড',
330
+ repeatPassword: 'পাসওয়ার্ড আবার লিখুন',
331
+ passwordRule: 'কমপক্ষে ১২ অক্ষর।',
332
+ mismatch: 'পাসওয়ার্ড দুটি মেলেনি।',
333
+ activate: 'সংরক্ষণ করে সাইন ইন করুন',
250
334
  signIn: 'সাইন ইন',
251
335
  login: 'লগইন',
252
336
  password: 'পাসওয়ার্ড',
@@ -256,11 +340,32 @@ export const bn = {
256
340
  signOut: 'সাইন আউট',
257
341
  invalid: 'লগইন বা পাসওয়ার্ড ভুল।',
258
342
  adminNeeds2fa: 'দ্বিতীয় ধাপ যাচাইয়ের পর অ্যাডমিনিস্ট্রেটরের অধিকার কার্যকর হবে।',
343
+ setupTitle: 'দুই-ধাপের সাইন-ইন চালু করুন',
344
+ setupIntro: 'এই প্রতিষ্ঠানে সাইন-ইনের সময় একটি দ্বিতীয় ধাপ লাগে: ফোনের অথেনটিকেটর অ্যাপের একটি কোড। চালিয়ে যেতে এটি চালু করুন।',
345
+ setupStart: 'চালু করুন',
346
+ setupStep1: 'অ্যাকাউন্টটি আপনার অথেনটিকেটর অ্যাপে যোগ করুন।',
347
+ setupOpenApp: 'অ্যাপে খুলুন',
348
+ setupStep2: 'অথবা এই কী লিখুন:',
349
+ setupStep3: 'অ্যাপে দেখানো ৬-সংখ্যার কোড দিন।',
350
+ setupTurnOn: 'চালু করে এগিয়ে যান',
351
+ setupCodes: 'এই রিকভারি কোডগুলো নিরাপদে রাখুন: ফোন হারালে প্রতিটি একবার সাইন-ইন করায়।',
352
+ setupContinue: 'এগিয়ে যান',
259
353
  },
260
354
  shell: {
261
355
  apps: 'অ্যাপস',
262
356
  menu: 'প্রধান মেনু',
263
357
  theme: 'থিম',
358
+ density: 'ঘনত্ব',
359
+ home: 'হোম',
360
+ appNamed: '{{app}} অ্যাপ',
361
+ switchCompany: 'প্রতিষ্ঠান: {{name}} (বদলান)',
362
+ sidebar: 'সাইডবার',
363
+ collapseSidebar: 'সাইডবার ছোট করুন',
364
+ expandSidebar: 'সাইডবার বড় করুন',
365
+ openMenu: 'মেনু খুলুন',
366
+ closeMenu: 'মেনু বন্ধ করুন',
367
+ menuHint: 'অ্যাপ ও তাদের অংশ',
368
+ location: 'আপনি এখানে আছেন',
264
369
  light: 'লাইট',
265
370
  dark: 'ডার্ক',
266
371
  system: 'সিস্টেম',
@@ -297,11 +402,26 @@ export const bn = {
297
402
  exportFailed: 'এক্সপোর্ট ব্যর্থ: {{reason}}',
298
403
  columns: 'কলাম',
299
404
  density: 'ঘনত্ব',
405
+ statusOf: '{{field}}: {{state}}',
406
+ nextState: 'পরবর্তী',
407
+ recordActions: 'রেকর্ডের কাজ',
408
+ summary: 'সারাংশ',
409
+ details: 'বিস্তারিত',
410
+ recordPanel: 'এই রেকর্ড সম্পর্কে',
411
+ showPanel: 'প্যানেল দেখান',
412
+ hidePanel: 'প্যানেল লুকান',
413
+ previousMonth: 'আগের মাস',
414
+ nextMonth: 'পরের মাস',
415
+ pickDate: 'তারিখ বাছুন',
416
+ datePlaceholder: 'তারিখ',
417
+ today: 'আজ',
418
+ clear: 'মুছে ফেলুন',
300
419
  proposed: 'প্রস্তাবিত',
301
420
  design: { addField: 'ফিল্ড যোগ করুন', addColumn: 'কলাম যোগ করুন' },
302
421
  compact: 'সংক্ষিপ্ত',
303
422
  comfortable: 'স্বাভাবিক',
304
423
  emptyTitle: 'এখানে এখনো কিছু নেই',
424
+ emptyNamed: 'এখনো কোনো {{name}} নেই',
305
425
  emptyHint: 'বর্তমান খোঁজের সাথে মেলা রেকর্ড এখানে দেখা যাবে।',
306
426
  emptyFiltered: 'বর্তমান খোঁজের সাথে কোনো রেকর্ড মেলেনি।',
307
427
  moved: '{{name}} {{column}}-এ সরানো হয়েছে',
@@ -420,9 +540,35 @@ export const bn = {
420
540
  done: 'সম্পন্ন',
421
541
  recent: 'সম্প্রতি খোলা',
422
542
  apps: 'অ্যাপস',
543
+ today: 'আজ, {{date}}',
544
+ approve: '{{record}} অনুমোদন করুন',
545
+ review: 'দেখুন',
546
+ dueToday: 'আজকের',
547
+ mentioned: '{{actor}} আপনাকে {{record}}-এ উল্লেখ করেছেন',
548
+ saved: 'আপনার সংরক্ষিত ভিউ',
423
549
  },
424
550
  toast: { region: 'বিজ্ঞপ্তি', close: 'বন্ধ করুন' },
551
+ image: {
552
+ choosePhoto: 'ছবি বদলান',
553
+ chooseLogo: 'লোগো বদলান',
554
+ remove: 'সরান',
555
+ hint: 'PNG, JPEG বা WebP। পাঠানোর আগে ছোট (২৫৬ px) করা হয়।',
556
+ unreadable: 'এই ফাইলটি ব্রাউজার পড়তে পারে এমন ছবি নয়।',
557
+ },
558
+ oauth: {
559
+ title: '{{app}} আপনার অ্যাকাউন্ট ব্যবহার করতে চায়',
560
+ as: '{{name}} ({{login}}) হিসেবে সাইন ইন করা।',
561
+ may: 'এটি যা করতে পারবে:',
562
+ limits: 'আপনি নিজে যা পারেন তার বেশি কখনো নয়। যেকোনো সময় সংযুক্ত অ্যাপস থেকে ফিরিয়ে নিতে পারেন।',
563
+ back: 'আপনি {{host}}-এ ফিরে যাবেন।',
564
+ homepage: 'অ্যাপ সম্পর্কে',
565
+ already: 'আপনি আগে এটি অনুমতি দিয়েছিলেন।',
566
+ allow: 'অনুমতি দিন',
567
+ cancel: 'বাতিল',
568
+ },
425
569
  palette: {
570
+ switchCompany: '{{name}}-এ যান',
571
+ profile: 'আপনার প্রোফাইল',
426
572
  button: 'খুঁজুন বা যান…',
427
573
  title: 'কমান্ড প্যালেট',
428
574
  description: 'রেকর্ড, মেনু ও কমান্ড খুঁজুন। তীর কী ও এন্টার ব্যবহার করুন।',
@@ -441,6 +587,7 @@ export const bn = {
441
587
  shortcuts: {
442
588
  title: 'কীবোর্ড শর্টকাট',
443
589
  palette: 'যেকোনো জায়গায় খুঁজুন বা যান',
590
+ sidebar: 'সাইডবার ছোট বা বড় করুন',
444
591
  search: 'সার্চ বক্সে যান',
445
592
  new: 'নতুন রেকর্ড',
446
593
  views: 'ভিউ বদলান',
@@ -467,6 +614,11 @@ export const bn = {
467
614
  custom: 'নিজস্ব ফিল্টার',
468
615
  remove: '{{facet}} সরান',
469
616
  or: 'অথবা',
617
+ savedViews: 'সংরক্ষিত ভিউ',
618
+ allRecords: 'সব',
619
+ moreViews_one: 'আরও {{count}}টি',
620
+ moreViews_other: 'আরও {{count}}টি',
621
+ shared: 'শেয়ার করা',
470
622
  },
471
623
  };
472
624
  export const ar = {
@@ -8,13 +8,15 @@
8
8
  */
9
9
  import { Registry } from './registry.js';
10
10
  export { ApiError, type ApiOptions, type AppEntry, type CurrentUser, createKoshApi, type KoshApi, type ListQuery, type MenuNode, NetworkError, type Problem, type Row, type ServedView, type Versioned, type ViewField, } from './api.js';
11
- export { KoshApp, type KoshAppProps } from './app.js';
11
+ export { KoshApp, type KoshAppProps, useLanguage } from './app.js';
12
12
  export { AppIcon } from './components/AppIcon.js';
13
+ export * from './components/Avatar.js';
13
14
  export * from './components/primitives.js';
14
15
  export { type KoshContextValue, KoshProvider, type ThemeChoice, useKosh, useTheme, } from './context.js';
15
16
  export { type DesignContextValue, DesignProvider, type DesignSpot, useDesign, } from './design.js';
16
17
  export { addMessages, createI18n, directionOf, NAMESPACE } from './i18n/index.js';
17
18
  export { LANGUAGES } from './i18n/messages.js';
19
+ export { accentVariables, hexToOklch } from './lib/accent.js';
18
20
  export { cn } from './lib/cn.js';
19
21
  export { bcp47, createFormatter, type Formatter } from './lib/format.js';
20
22
  export { type LiveRecord, useLiveModel, useLiveRecord } from './live.js';
package/dist/ui/index.js CHANGED
@@ -12,13 +12,15 @@ import { KanbanView } from './views/KanbanView.js';
12
12
  import { ListView } from './views/ListView.js';
13
13
  import { registerDefaultWidgets } from './views/widgets.js';
14
14
  export { ApiError, createKoshApi, NetworkError, } from './api.js';
15
- export { KoshApp } from './app.js';
15
+ export { KoshApp, useLanguage } from './app.js';
16
16
  export { AppIcon } from './components/AppIcon.js';
17
+ export * from './components/Avatar.js';
17
18
  export * from './components/primitives.js';
18
19
  export { KoshProvider, useKosh, useTheme, } from './context.js';
19
20
  export { DesignProvider, useDesign, } from './design.js';
20
21
  export { addMessages, createI18n, directionOf, NAMESPACE } from './i18n/index.js';
21
22
  export { LANGUAGES } from './i18n/messages.js';
23
+ export { accentVariables, hexToOklch } from './lib/accent.js';
22
24
  export { cn } from './lib/cn.js';
23
25
  export { bcp47, createFormatter } from './lib/format.js';
24
26
  export { useLiveModel, useLiveRecord } from './live.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A company's brand colour as the back-office's accent (RFC 0018 §4.5). Only its hue and chroma are used: the
3
+ * themes fix the accent's lightness (light: dark enough for white text; dark: light enough for dark text), so any
4
+ * brand colour keeps text on buttons and links readable (WCAG AA) in both.
5
+ */
6
+ /** OKLCH of an sRGB hex colour (`#4f46e5` → `{ l, c, h }`), or null when it is not one. */
7
+ export declare function hexToOklch(hex: string): {
8
+ l: number;
9
+ c: number;
10
+ h: number;
11
+ } | null;
12
+ /** The CSS variables a brand colour sets (`--accent-h`, `--accent-c`), or none for the default accent. */
13
+ export declare function accentVariables(hex: string | null | undefined): Record<string, string>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A company's brand colour as the back-office's accent (RFC 0018 §4.5). Only its hue and chroma are used: the
3
+ * themes fix the accent's lightness (light: dark enough for white text; dark: light enough for dark text), so any
4
+ * brand colour keeps text on buttons and links readable (WCAG AA) in both.
5
+ */
6
+ /** OKLCH of an sRGB hex colour (`#4f46e5` → `{ l, c, h }`), or null when it is not one. */
7
+ export function hexToOklch(hex) {
8
+ const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
9
+ if (m === null)
10
+ return null;
11
+ const linear = (v) => {
12
+ const x = Number.parseInt(v, 16) / 255;
13
+ return x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
14
+ };
15
+ const [r, g, b] = [linear(m[1]), linear(m[2]), linear(m[3])];
16
+ const l_ = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
17
+ const m_ = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
18
+ const s_ = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
19
+ const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;
20
+ const A = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;
21
+ const B = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;
22
+ const c = Math.sqrt(A * A + B * B);
23
+ const h = (Math.atan2(B, A) * 180) / Math.PI;
24
+ return { l: L, c, h: h < 0 ? h + 360 : h };
25
+ }
26
+ /** The CSS variables a brand colour sets (`--accent-h`, `--accent-c`), or none for the default accent. */
27
+ export function accentVariables(hex) {
28
+ const color = hex === null || hex === undefined ? null : hexToOklch(hex);
29
+ if (color === null)
30
+ return {};
31
+ // Chroma is capped (very saturated colours glare as large surfaces); greys stay grey.
32
+ return {
33
+ '--accent-h': color.h.toFixed(1),
34
+ '--accent-c': Math.min(color.c, 0.2).toFixed(3),
35
+ };
36
+ }
@@ -124,9 +124,13 @@ export declare class Registry {
124
124
  /** The widget for a node: its `widget`, else the field kind, else plain text. */
125
125
  widget(node: FieldNode, field: ViewField): ComponentType<WidgetProps>;
126
126
  component(name: string): ComponentType<NodeComponentProps> | undefined;
127
- /** An item of the shell's header, before the language and theme menus (`notify.Bell`). */
127
+ /**
128
+ * An item of the shell's header, before the language and theme menus (`notify.Bell`). With `home`, it is also in
129
+ * the home page's corner, next to the account menu (notifications, messages).
130
+ */
128
131
  registerHeaderItem(name: string, component: ComponentType<HeaderItemProps>, options?: {
129
132
  readonly replace?: boolean;
133
+ readonly home?: boolean;
130
134
  }): this;
131
135
  /**
132
136
  * A personal page every signed-in user reaches from the user menu, outside any app (`settings.ApiKeys`):
@@ -147,6 +151,8 @@ export declare class Registry {
147
151
  headerItems(): readonly {
148
152
  readonly name: string;
149
153
  readonly component: ComponentType<HeaderItemProps>;
154
+ /** Also on the home page. */
155
+ readonly home: boolean;
150
156
  }[];
151
157
  /** A control next to every search box (the AI assistant's "Ask"). */
152
158
  registerSearchAssist(name: string, component: ComponentType<SearchAssistProps>, options?: {
@@ -55,8 +55,16 @@ export class Registry {
55
55
  component(name) {
56
56
  return this.#entries.component.get(name);
57
57
  }
58
- /** An item of the shell's header, before the language and theme menus (`notify.Bell`). */
58
+ #homeItems = new Set();
59
+ /**
60
+ * An item of the shell's header, before the language and theme menus (`notify.Bell`). With `home`, it is also in
61
+ * the home page's corner, next to the account menu (notifications, messages).
62
+ */
59
63
  registerHeaderItem(name, component, options = {}) {
64
+ if (options.home === true)
65
+ this.#homeItems.add(name);
66
+ else
67
+ this.#homeItems.delete(name);
60
68
  return this.#add('header', name, component, options.replace === true);
61
69
  }
62
70
  #userPageLabels = new Map();
@@ -83,6 +91,7 @@ export class Registry {
83
91
  return [...this.#entries.header].map(([name, component]) => ({
84
92
  name,
85
93
  component: component,
94
+ home: this.#homeItems.has(name),
86
95
  }));
87
96
  }
88
97
  /** A control next to every search box (the AI assistant's "Ask"). */
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Saved views as tabs (RFC 0018 §4.3): above the search, *All* and the saved searches of this model and view type —
3
+ * the user's own and the shared ones —, the one matching the current search marked. A tab applies its search; more
4
+ * than a handful go into a *More* menu. Saving and the full list stay in the search bar's favorites menu; with
5
+ * nothing saved, there are no tabs.
6
+ */
7
+ import { type SearchState } from '../../search/index.js';
8
+ import type { ReactElement } from 'react';
9
+ export declare function ViewTabs({ model, viewType, state, onChange, }: {
10
+ readonly model: string;
11
+ readonly viewType: string;
12
+ readonly state: SearchState;
13
+ readonly onChange: (state: SearchState) => void;
14
+ }): ReactElement | null;