shutro-erp 0.3.2 → 0.3.3

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 (130) hide show
  1. package/dist/account_portal/.kosh/bindings.js +1 -1
  2. package/dist/account_portal/.kosh/schema.d.ts +29 -1
  3. package/dist/accounting/.kosh/bindings.js +1 -1
  4. package/dist/accounting/.kosh/schema.d.ts +29 -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 +29 -1
  8. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  9. package/dist/l10n_bd/.kosh/schema.d.ts +29 -1
  10. package/dist/l10n_in/.kosh/bindings.js +1 -1
  11. package/dist/l10n_in/.kosh/schema.d.ts +29 -4
  12. package/dist/l10n_in/module.d.ts +0 -62
  13. package/dist/l10n_in/module.js +2 -4
  14. package/dist/payment/.kosh/bindings.js +1 -1
  15. package/dist/payment/.kosh/schema.d.ts +30 -2
  16. package/dist/payment/bkash.d.ts +28 -0
  17. package/dist/payment/bkash.js +169 -0
  18. package/dist/payment/index.d.ts +4 -0
  19. package/dist/payment/index.js +80 -24
  20. package/dist/payment/module.d.ts +7 -4
  21. package/dist/payment/module.js +3 -0
  22. package/dist/payment/money.d.ts +7 -0
  23. package/dist/payment/money.js +37 -0
  24. package/dist/payment/providers.d.ts +13 -2
  25. package/dist/payment/razorpay.d.ts +12 -0
  26. package/dist/payment/razorpay.js +149 -0
  27. package/dist/payment/sslcommerz.js +1 -1
  28. package/dist/payment/stripe.d.ts +15 -0
  29. package/dist/payment/stripe.js +182 -0
  30. package/dist/permissions/admin.d.ts +3 -0
  31. package/dist/permissions/admin.js +108 -10
  32. package/dist/permissions/index.d.ts +1 -0
  33. package/dist/permissions/index.js +3 -0
  34. package/dist/permissions/roles.d.ts +2 -0
  35. package/dist/permissions/roles.js +236 -0
  36. package/dist/portal/.kosh/bindings.js +1 -1
  37. package/dist/portal/.kosh/schema.d.ts +29 -1
  38. package/dist/product/.kosh/bindings.js +1 -1
  39. package/dist/product/.kosh/schema.d.ts +29 -1
  40. package/dist/project/.kosh/bindings.js +1 -1
  41. package/dist/project/.kosh/schema.d.ts +29 -1
  42. package/dist/project_portal/.kosh/bindings.js +1 -1
  43. package/dist/project_portal/.kosh/schema.d.ts +29 -1
  44. package/dist/purchase/.kosh/bindings.js +1 -1
  45. package/dist/purchase/.kosh/schema.d.ts +29 -1
  46. package/dist/purchase_account/.kosh/bindings.js +1 -1
  47. package/dist/purchase_account/.kosh/schema.d.ts +29 -1
  48. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  49. package/dist/purchase_portal/.kosh/schema.d.ts +29 -1
  50. package/dist/sale_account/.kosh/bindings.js +1 -1
  51. package/dist/sale_account/.kosh/schema.d.ts +29 -1
  52. package/dist/sale_portal/.kosh/bindings.js +1 -1
  53. package/dist/sale_portal/.kosh/schema.d.ts +29 -1
  54. package/dist/sale_project/.kosh/bindings.js +1 -1
  55. package/dist/sale_project/.kosh/schema.d.ts +29 -1
  56. package/dist/sales/.kosh/bindings.js +1 -1
  57. package/dist/sales/.kosh/schema.d.ts +29 -1
  58. package/dist/server/auth/base.js +22 -0
  59. package/dist/server/auth/routes.d.ts +3 -0
  60. package/dist/server/auth/routes.js +29 -0
  61. package/dist/server/auth/service.d.ts +52 -0
  62. package/dist/server/auth/service.js +168 -3
  63. package/dist/server/identity.d.ts +2 -0
  64. package/dist/server/index.d.ts +1 -1
  65. package/dist/server/server.js +11 -3
  66. package/dist/settings/company.d.ts +34 -0
  67. package/dist/settings/company.js +187 -0
  68. package/dist/settings/index.d.ts +10 -10
  69. package/dist/settings/index.js +159 -30
  70. package/dist/settings/people.d.ts +16 -0
  71. package/dist/settings/people.js +505 -0
  72. package/dist/settings/security.d.ts +13 -0
  73. package/dist/settings/security.js +158 -0
  74. package/dist/settings/web/AuditPage.d.ts +8 -0
  75. package/dist/settings/web/AuditPage.js +74 -0
  76. package/dist/settings/web/CompaniesPage.d.ts +8 -0
  77. package/dist/settings/web/CompaniesPage.js +52 -0
  78. package/dist/settings/web/CompanyPage.d.ts +9 -0
  79. package/dist/settings/web/CompanyPage.js +94 -0
  80. package/dist/settings/web/OverviewPage.d.ts +9 -0
  81. package/dist/settings/web/OverviewPage.js +66 -0
  82. package/dist/settings/web/PeoplePage.d.ts +9 -0
  83. package/dist/settings/web/PeoplePage.js +163 -0
  84. package/dist/settings/web/ProfilePage.d.ts +14 -0
  85. package/dist/settings/web/ProfilePage.js +194 -0
  86. package/dist/settings/web/RolesEditor.d.ts +35 -0
  87. package/dist/settings/web/RolesEditor.js +49 -0
  88. package/dist/settings/web/RolesPage.d.ts +9 -0
  89. package/dist/settings/web/RolesPage.js +296 -0
  90. package/dist/settings/web/SecurityPage.d.ts +8 -0
  91. package/dist/settings/web/SecurityPage.js +81 -0
  92. package/dist/settings/web/index.d.ts +9 -0
  93. package/dist/settings/web/index.js +26 -1
  94. package/dist/settings/web/messages.js +566 -0
  95. package/dist/ui/api.d.ts +4 -0
  96. package/dist/ui/app.d.ts +5 -0
  97. package/dist/ui/app.js +108 -31
  98. package/dist/ui/components/primitives.d.ts +9 -1
  99. package/dist/ui/components/primitives.js +12 -4
  100. package/dist/ui/context.d.ts +10 -1
  101. package/dist/ui/context.js +60 -2
  102. package/dist/ui/i18n/messages.d.ts +46 -0
  103. package/dist/ui/i18n/messages.js +92 -0
  104. package/dist/ui/index.d.ts +2 -1
  105. package/dist/ui/index.js +2 -1
  106. package/dist/ui/lib/accent.d.ts +13 -0
  107. package/dist/ui/lib/accent.js +36 -0
  108. package/dist/ui/shell/Activate.d.ts +6 -0
  109. package/dist/ui/shell/Activate.js +39 -0
  110. package/dist/ui/shell/CommandPalette.d.ts +7 -1
  111. package/dist/ui/shell/CommandPalette.js +16 -2
  112. package/dist/ui/shell/Sidebar.d.ts +27 -0
  113. package/dist/ui/shell/Sidebar.js +84 -0
  114. package/dist/ui/shell/TwoFactorSetup.d.ts +5 -0
  115. package/dist/ui/shell/TwoFactorSetup.js +32 -0
  116. package/dist/ui/shell/shortcuts.d.ts +3 -0
  117. package/dist/ui/shell/shortcuts.js +13 -4
  118. package/dist/ui/styles.css +70 -28
  119. package/dist/ui/views/DateField.d.ts +10 -0
  120. package/dist/ui/views/DateField.js +129 -0
  121. package/dist/ui/views/FormView.js +161 -21
  122. package/dist/ui/views/ListView.d.ts +2 -0
  123. package/dist/ui/views/ListView.js +8 -3
  124. package/dist/ui/views/StatusChip.d.ts +10 -0
  125. package/dist/ui/views/StatusChip.js +22 -0
  126. package/dist/ui/views/widgets.d.ts +3 -0
  127. package/dist/ui/views/widgets.js +26 -8
  128. package/dist/views/types.d.ts +7 -0
  129. package/dist/views/validate.js +4 -0
  130. package/package.json +2 -2
@@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next';
11
11
  import { Badge, Checkbox, Input, Select, Textarea } from '../components/primitives.js';
12
12
  import { useKosh } from '../context.js';
13
13
  import { cn } from '../lib/cn.js';
14
+ import { DateField } from './DateField.js';
14
15
  const text = (v) => (v === null || v === undefined ? '' : String(v));
15
16
  function CharWidget({ id, value, readonly, onChange, field, invalid }) {
16
17
  if (onChange === undefined || readonly)
@@ -56,11 +57,11 @@ function BooleanWidget({ id, value, readonly, onChange, field }) {
56
57
  return _jsx("span", { children: value === true ? t('views.yes') : t('views.no') });
57
58
  return (_jsx(Checkbox, { id: id, checked: value === true, disabled: readonly, "aria-label": field.label, onCheckedChange: (c) => onChange(c === true) }));
58
59
  }
59
- function DateWidget({ id, value, readonly, onChange }) {
60
+ function DateWidget({ id, value, readonly, onChange, field, invalid }) {
60
61
  const { format } = useKosh();
61
62
  if (onChange === undefined || readonly)
62
63
  return _jsx("span", { children: format.date(value === null || value === undefined ? null : String(value)) });
63
- return (_jsx(Input, { id: id, type: "date", value: text(value).slice(0, 10), onChange: (e) => onChange(e.target.value === '' ? null : e.target.value) }));
64
+ return (_jsx(DateField, { id: id, value: value === null || value === undefined || value === '' ? null : text(value).slice(0, 10), onChange: onChange, required: field.required, ...(invalid === undefined ? {} : { invalid }) }));
64
65
  }
65
66
  function DatetimeWidget({ id, value, readonly, onChange }) {
66
67
  const { format } = useKosh();
@@ -80,14 +81,31 @@ function SelectionWidget({ id, value, readonly, onChange, field }) {
80
81
  return _jsx("span", { children: label });
81
82
  return (_jsx(Select, { id: id, label: field.label, value: value === null || value === undefined ? null : String(value), options: options, onChange: onChange }));
82
83
  }
83
- const TONES = ['info', 'success', 'danger', 'warning', 'neutral'];
84
+ /** What a state's value means, by the words ERP states use (`posted`, `to_approve`, `cancel`…). */
85
+ const MEANINGS = [
86
+ [
87
+ /(^|_)(cancel|cancelled|canceled|failed|refused|rejected|lost|blocked|overdue|error|bounced)($|_)/,
88
+ 'danger',
89
+ ],
90
+ [
91
+ /(^|_)(to|not|partial|partially|pending|waiting|wait|hold|review|approve|quarantine)($|_)/,
92
+ 'warning',
93
+ ],
94
+ [
95
+ /(^|_)(done|paid|posted|sale|confirmed|approved|won|delivered|validated|completed|complete|reconciled|invoiced|handled|active|accepted|signed|locked)($|_)/,
96
+ 'success',
97
+ ],
98
+ [/(^|_)(sent|open|progress|received|in|scheduled|running|purchase|ordered)($|_)/, 'info'],
99
+ ];
100
+ export function toneOf(value) {
101
+ const v = value.toLowerCase();
102
+ return MEANINGS.find(([pattern]) => pattern.test(v))?.[1] ?? 'neutral';
103
+ }
84
104
  function BadgeWidget({ value, field }) {
85
105
  if (value === null || value === undefined || value === '')
86
106
  return null;
87
- const options = field.options ?? [];
88
- const index = options.findIndex((o) => o.value === value);
89
- const label = options[index]?.label ?? text(value);
90
- return _jsx(Badge, { tone: TONES[Math.max(index, 0) % TONES.length] ?? 'neutral', children: label });
107
+ const label = (field.options ?? []).find((o) => o.value === value)?.label ?? text(value);
108
+ return _jsx(Badge, { tone: toneOf(text(value)), children: label });
91
109
  }
92
110
  /** Display names a list or kanban loaded in one batch per model, so cells do not query one by one. */
93
111
  export const PrefetchedNames = createContext(new Map());
@@ -166,7 +184,7 @@ function Many2oneWidget({ id, value, readonly, onChange, field, invalid, }) {
166
184
  const label = current === null ? '' : (names.get(current) ?? `#${current}`);
167
185
  if (onChange === undefined || readonly)
168
186
  return _jsx("span", { children: label });
169
- return (_jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx("button", { id: id, type: "button", role: "combobox", "aria-expanded": open, "aria-controls": listId, "aria-invalid": invalid === true ? true : undefined, className: "flex h-9 w-full items-center justify-between rounded-md border border-input bg-surface px-3 text-start text-sm aria-invalid:border-danger", children: _jsx("span", { className: cn(current === null && 'text-muted-foreground'), children: current === null ? t('views.choose') : label }) }) }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "start", sideOffset: 4, className: "z-50 w-[var(--radix-popover-trigger-width)] rounded-md border bg-surface p-1 shadow-lg", children: [_jsx(Input, { autoFocus: true, "aria-label": field.label, placeholder: t('search.placeholder'), value: q, onChange: (e) => setQ(e.target.value) }), _jsxs("div", { id: listId, role: "listbox", "aria-label": field.label, className: "mt-1 max-h-60 overflow-auto", children: [field.required ? null : (_jsx("button", { type: "button", role: "option", "aria-selected": current === null, className: "w-full rounded-sm px-2 py-1.5 text-start text-sm text-muted-foreground hover:bg-muted", onClick: () => {
187
+ return (_jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx("button", { id: id, type: "button", role: "combobox", "aria-expanded": open, "aria-controls": listId, "aria-invalid": invalid === true ? true : undefined, className: "flex h-[var(--control-h)] w-full items-center justify-between rounded-md border border-input bg-surface px-3 text-start text-sm aria-invalid:border-danger", children: _jsx("span", { className: cn(current === null && 'text-muted-foreground'), children: current === null ? t('views.choose') : label }) }) }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "start", sideOffset: 4, className: "z-50 w-[var(--radix-popover-trigger-width)] rounded-md border bg-surface p-1 shadow-lg", children: [_jsx(Input, { autoFocus: true, "aria-label": field.label, placeholder: t('search.placeholder'), value: q, onChange: (e) => setQ(e.target.value) }), _jsxs("div", { id: listId, role: "listbox", "aria-label": field.label, className: "mt-1 max-h-60 overflow-auto", children: [field.required ? null : (_jsx("button", { type: "button", role: "option", "aria-selected": current === null, className: "w-full rounded-sm px-2 py-1.5 text-start text-sm text-muted-foreground hover:bg-muted", onClick: () => {
170
188
  onChange(null);
171
189
  setOpen(false);
172
190
  }, children: t('views.none') })), (results.data ?? []).map((r) => (_jsx("button", { type: "button", role: "option", "aria-selected": r.id === current, className: "w-full rounded-sm px-2 py-1.5 text-start text-sm hover:bg-muted", onClick: () => {
@@ -96,6 +96,13 @@ export interface FormView extends ViewBase {
96
96
  readonly readonlyWhen?: Condition;
97
97
  /** Messages and activities (RFC 0006). */
98
98
  readonly chatter?: boolean;
99
+ /**
100
+ * The key facts under the title (RFC 0018 §4.2): the customer, the date, the total. Default: the first many2one,
101
+ * date and monetary fields of the form.
102
+ */
103
+ readonly subtitle?: readonly string[];
104
+ /** The fields of the right rail's Summary (RFC 0018 §4.2). Default: the group named `totals`. */
105
+ readonly summary?: readonly string[];
99
106
  }
100
107
  export interface KanbanCard {
101
108
  readonly title: string;
@@ -58,6 +58,10 @@ export function validateView(view, models) {
58
58
  if (node.kind === 'button' && !info.transitions.includes(node.transition))
59
59
  problems.push(`unknown transition '${node.transition}' (has ${info.transitions.join(', ') || 'none'})`);
60
60
  }
61
+ if (view.type === 'form')
62
+ for (const key of [...(view.subtitle ?? []), ...(view.summary ?? [])])
63
+ if (!info.fields.has(key))
64
+ problems.push(`unknown field '${key}' in the subtitle or summary`);
61
65
  if (view.type === 'form' && view.header.statusbar !== undefined) {
62
66
  const kind = info.fields.get(view.header.statusbar)?.kind;
63
67
  if (kind !== 'state' && kind !== 'selection')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shutro-erp",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Shutro ERP (Kosh): a modern ERP framework on TypeScript, PostgreSQL and the Shutro ORM — typed end to end, permissions enforced on the server, metadata-driven views, reporting, documents and a chatter on every record.",
5
5
  "keywords": [
6
6
  "erp",
@@ -289,7 +289,7 @@
289
289
  "postal-mime": "^4.1.4",
290
290
  "radix-ui": "^1.8.1",
291
291
  "sanitize-html": "^2.18.1",
292
- "shutro": "^0.3.2",
292
+ "shutro": "^0.3.3",
293
293
  "tailwind-merge": "^3.7.0",
294
294
  "web-push": "^3.6.7"
295
295
  },