@open-mercato/ui 0.4.6-develop-0c668dfcae → 0.4.6-develop-91451bbf8d

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.
@@ -4,70 +4,24 @@ import * as React from "react";
4
4
  import { Send } from "lucide-react";
5
5
  import { useT } from "@open-mercato/shared/lib/i18n/context";
6
6
  import { Button } from "../../primitives/button.js";
7
- import { apiCall } from "../utils/apiCall.js";
8
7
  import {
9
8
  MessageComposer
10
9
  } from "./MessageComposer.js";
11
- let composeAccessCache = null;
12
- let composeAccessPromise = null;
13
- async function readComposeAccess() {
14
- if (composeAccessCache !== null) return composeAccessCache;
15
- if (composeAccessPromise) return composeAccessPromise;
16
- composeAccessPromise = (async () => {
17
- try {
18
- const call = await apiCall("/api/auth/feature-check", {
19
- method: "POST",
20
- headers: { "content-type": "application/json" },
21
- body: JSON.stringify({ features: ["messages.compose"] })
22
- });
23
- const granted = Array.isArray(call.result?.granted) ? call.result.granted : [];
24
- const canCompose = granted.includes("messages.compose");
25
- composeAccessCache = canCompose;
26
- return canCompose;
27
- } catch {
28
- composeAccessCache = false;
29
- return false;
30
- } finally {
31
- composeAccessPromise = null;
32
- }
33
- })();
34
- return composeAccessPromise;
35
- }
36
10
  function SendObjectMessageDialog({
37
11
  object,
38
12
  defaultValues,
39
13
  lockedType = "messages.defaultWithObjects",
40
14
  requiredActionConfig = null,
41
15
  disabled = false,
42
- canCompose,
43
16
  viewHref: _viewHref = null,
44
17
  onSuccess
45
18
  }) {
46
19
  const t = useT();
47
20
  const [open, setOpen] = React.useState(false);
48
- const [composeEnabled, setComposeEnabled] = React.useState(canCompose ?? false);
49
- const [composeAccessResolved, setComposeAccessResolved] = React.useState(canCompose !== void 0);
50
- React.useEffect(() => {
51
- if (canCompose !== void 0) {
52
- setComposeEnabled(canCompose);
53
- setComposeAccessResolved(true);
54
- return;
55
- }
56
- let mounted = true;
57
- void (async () => {
58
- const allowed = await readComposeAccess();
59
- if (!mounted) return;
60
- setComposeEnabled(allowed);
61
- setComposeAccessResolved(true);
62
- })();
63
- return () => {
64
- mounted = false;
65
- };
66
- }, [canCompose]);
67
21
  const openComposer = React.useCallback(() => {
68
- if (disabled || !composeEnabled) return;
22
+ if (disabled) return;
69
23
  setOpen(true);
70
- }, [disabled, composeEnabled]);
24
+ }, [disabled]);
71
25
  const contextObject = React.useMemo(() => ({
72
26
  entityModule: object.entityModule,
73
27
  entityType: object.entityType,
@@ -76,9 +30,6 @@ function SendObjectMessageDialog({
76
30
  sourceEntityId: object.sourceEntityId ?? null,
77
31
  previewData: object.previewData ?? null
78
32
  }), [object.entityId, object.entityModule, object.entityType, object.sourceEntityId, object.sourceEntityType, object.previewData]);
79
- if (!composeAccessResolved || !composeEnabled) {
80
- return null;
81
- }
82
33
  return /* @__PURE__ */ jsxs(Fragment, { children: [
83
34
  /* @__PURE__ */ jsx(
84
35
  Button,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/messages/SendObjectMessageDialog.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Send } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport { apiCall } from '../utils/apiCall'\nimport {\n MessageComposer,\n type MessageComposerContextObject,\n type MessageComposerProps,\n type MessageComposerRequiredActionConfig,\n} from './MessageComposer'\n\nexport type SendObjectMessageDialogProps = {\n object: MessageComposerContextObject\n defaultValues?: MessageComposerProps['defaultValues']\n lockedType?: string | null\n requiredActionConfig?: MessageComposerRequiredActionConfig | null\n disabled?: boolean\n canCompose?: boolean\n viewHref?: string | null\n onSuccess?: MessageComposerProps['onSuccess']\n}\n\ntype FeatureCheckResponse = {\n granted?: string[]\n}\n\nlet composeAccessCache: boolean | null = null\nlet composeAccessPromise: Promise<boolean> | null = null\n\nasync function readComposeAccess(): Promise<boolean> {\n if (composeAccessCache !== null) return composeAccessCache\n if (composeAccessPromise) return composeAccessPromise\n\n composeAccessPromise = (async () => {\n try {\n const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['messages.compose'] }),\n })\n const granted = Array.isArray(call.result?.granted) ? call.result.granted : []\n const canCompose = granted.includes('messages.compose')\n composeAccessCache = canCompose\n return canCompose\n } catch {\n composeAccessCache = false\n return false\n } finally {\n composeAccessPromise = null\n }\n })()\n\n return composeAccessPromise\n}\n\nexport function SendObjectMessageDialog({\n object,\n defaultValues,\n lockedType = 'messages.defaultWithObjects',\n requiredActionConfig = null,\n disabled = false,\n canCompose,\n viewHref: _viewHref = null,\n onSuccess,\n}: SendObjectMessageDialogProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [composeEnabled, setComposeEnabled] = React.useState<boolean>(canCompose ?? false)\n const [composeAccessResolved, setComposeAccessResolved] = React.useState<boolean>(canCompose !== undefined)\n\n React.useEffect(() => {\n if (canCompose !== undefined) {\n setComposeEnabled(canCompose)\n setComposeAccessResolved(true)\n return\n }\n\n let mounted = true\n void (async () => {\n const allowed = await readComposeAccess()\n if (!mounted) return\n setComposeEnabled(allowed)\n setComposeAccessResolved(true)\n })()\n\n return () => {\n mounted = false\n }\n }, [canCompose])\n\n const openComposer = React.useCallback(() => {\n if (disabled || !composeEnabled) return\n setOpen(true)\n }, [disabled, composeEnabled])\n const contextObject = React.useMemo(() => ({\n entityModule: object.entityModule,\n entityType: object.entityType,\n entityId: object.entityId,\n sourceEntityType: object.sourceEntityType ?? null,\n sourceEntityId: object.sourceEntityId ?? null,\n previewData: object.previewData ?? null,\n }), [object.entityId, object.entityModule, object.entityType, object.sourceEntityId, object.sourceEntityType, object.previewData])\n\n if (!composeAccessResolved || !composeEnabled) {\n return null\n }\n\n return (\n <>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={disabled}\n onClick={openComposer}\n aria-label={t('messages.compose', 'Compose message')}\n title={t('messages.compose', 'Compose message')}\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n <MessageComposer\n variant=\"compose\"\n open={open}\n onOpenChange={setOpen}\n lockedType={lockedType}\n contextObject={contextObject}\n requiredActionConfig={requiredActionConfig}\n defaultValues={defaultValues}\n onSuccess={onSuccess}\n />\n </>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Send } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '../../primitives/button'\nimport {\n MessageComposer,\n type MessageComposerContextObject,\n type MessageComposerProps,\n type MessageComposerRequiredActionConfig,\n} from './MessageComposer'\n\nexport type SendObjectMessageDialogProps = {\n object: MessageComposerContextObject\n defaultValues?: MessageComposerProps['defaultValues']\n lockedType?: string | null\n requiredActionConfig?: MessageComposerRequiredActionConfig | null\n disabled?: boolean\n viewHref?: string | null\n onSuccess?: MessageComposerProps['onSuccess']\n}\n\nexport function SendObjectMessageDialog({\n object,\n defaultValues,\n lockedType = 'messages.defaultWithObjects',\n requiredActionConfig = null,\n disabled = false,\n viewHref: _viewHref = null,\n onSuccess,\n}: SendObjectMessageDialogProps) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n\n const openComposer = React.useCallback(() => {\n if (disabled) return\n setOpen(true)\n }, [disabled])\n const contextObject = React.useMemo(() => ({\n entityModule: object.entityModule,\n entityType: object.entityType,\n entityId: object.entityId,\n sourceEntityType: object.sourceEntityType ?? null,\n sourceEntityId: object.sourceEntityId ?? null,\n previewData: object.previewData ?? null,\n }), [object.entityId, object.entityModule, object.entityType, object.sourceEntityId, object.sourceEntityType, object.previewData])\n\n return (\n <>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n disabled={disabled}\n onClick={openComposer}\n aria-label={t('messages.compose', 'Compose message')}\n title={t('messages.compose', 'Compose message')}\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n <MessageComposer\n variant=\"compose\"\n open={open}\n onOpenChange={setOpen}\n lockedType={lockedType}\n contextObject={contextObject}\n requiredActionConfig={requiredActionConfig}\n defaultValues={defaultValues}\n onSuccess={onSuccess}\n />\n </>\n )\n}\n"],
5
+ "mappings": ";AAiDI,mBAUI,KAVJ;AA/CJ,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,OAIK;AAYA,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,uBAAuB;AAAA,EACvB,WAAW;AAAA,EACX,UAAU,YAAY;AAAA,EACtB;AACF,GAAiC;AAC/B,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAE5C,QAAM,eAAe,MAAM,YAAY,MAAM;AAC3C,QAAI,SAAU;AACd,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,CAAC;AACb,QAAM,gBAAgB,MAAM,QAAQ,OAAO;AAAA,IACzC,cAAc,OAAO;AAAA,IACrB,YAAY,OAAO;AAAA,IACnB,UAAU,OAAO;AAAA,IACjB,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,gBAAgB,OAAO,kBAAkB;AAAA,IACzC,aAAa,OAAO,eAAe;AAAA,EACrC,IAAI,CAAC,OAAO,UAAU,OAAO,cAAc,OAAO,YAAY,OAAO,gBAAgB,OAAO,kBAAkB,OAAO,WAAW,CAAC;AAEjI,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,SAAQ;AAAA,QACR;AAAA,QACA,SAAS;AAAA,QACT,cAAY,EAAE,oBAAoB,iBAAiB;AAAA,QACnD,OAAO,EAAE,oBAAoB,iBAAiB;AAAA,QAE9C,8BAAC,QAAK,WAAU,WAAU;AAAA;AAAA,IAC5B;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR;AAAA,QACA,cAAc;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.4.6-develop-0c668dfcae",
3
+ "version": "0.4.6-develop-91451bbf8d",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -116,7 +116,7 @@
116
116
  }
117
117
  },
118
118
  "dependencies": {
119
- "@open-mercato/shared": "0.4.6-develop-0c668dfcae",
119
+ "@open-mercato/shared": "0.4.6-develop-91451bbf8d",
120
120
  "@radix-ui/react-popover": "^1.1.6",
121
121
  "@radix-ui/react-tooltip": "^1.2.8",
122
122
  "date-fns": "^4.1.0",
@@ -4,7 +4,6 @@ import * as React from 'react'
4
4
  import { Send } from 'lucide-react'
5
5
  import { useT } from '@open-mercato/shared/lib/i18n/context'
6
6
  import { Button } from '../../primitives/button'
7
- import { apiCall } from '../utils/apiCall'
8
7
  import {
9
8
  MessageComposer,
10
9
  type MessageComposerContextObject,
@@ -18,83 +17,26 @@ export type SendObjectMessageDialogProps = {
18
17
  lockedType?: string | null
19
18
  requiredActionConfig?: MessageComposerRequiredActionConfig | null
20
19
  disabled?: boolean
21
- canCompose?: boolean
22
20
  viewHref?: string | null
23
21
  onSuccess?: MessageComposerProps['onSuccess']
24
22
  }
25
23
 
26
- type FeatureCheckResponse = {
27
- granted?: string[]
28
- }
29
-
30
- let composeAccessCache: boolean | null = null
31
- let composeAccessPromise: Promise<boolean> | null = null
32
-
33
- async function readComposeAccess(): Promise<boolean> {
34
- if (composeAccessCache !== null) return composeAccessCache
35
- if (composeAccessPromise) return composeAccessPromise
36
-
37
- composeAccessPromise = (async () => {
38
- try {
39
- const call = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {
40
- method: 'POST',
41
- headers: { 'content-type': 'application/json' },
42
- body: JSON.stringify({ features: ['messages.compose'] }),
43
- })
44
- const granted = Array.isArray(call.result?.granted) ? call.result.granted : []
45
- const canCompose = granted.includes('messages.compose')
46
- composeAccessCache = canCompose
47
- return canCompose
48
- } catch {
49
- composeAccessCache = false
50
- return false
51
- } finally {
52
- composeAccessPromise = null
53
- }
54
- })()
55
-
56
- return composeAccessPromise
57
- }
58
-
59
24
  export function SendObjectMessageDialog({
60
25
  object,
61
26
  defaultValues,
62
27
  lockedType = 'messages.defaultWithObjects',
63
28
  requiredActionConfig = null,
64
29
  disabled = false,
65
- canCompose,
66
30
  viewHref: _viewHref = null,
67
31
  onSuccess,
68
32
  }: SendObjectMessageDialogProps) {
69
33
  const t = useT()
70
34
  const [open, setOpen] = React.useState(false)
71
- const [composeEnabled, setComposeEnabled] = React.useState<boolean>(canCompose ?? false)
72
- const [composeAccessResolved, setComposeAccessResolved] = React.useState<boolean>(canCompose !== undefined)
73
-
74
- React.useEffect(() => {
75
- if (canCompose !== undefined) {
76
- setComposeEnabled(canCompose)
77
- setComposeAccessResolved(true)
78
- return
79
- }
80
-
81
- let mounted = true
82
- void (async () => {
83
- const allowed = await readComposeAccess()
84
- if (!mounted) return
85
- setComposeEnabled(allowed)
86
- setComposeAccessResolved(true)
87
- })()
88
-
89
- return () => {
90
- mounted = false
91
- }
92
- }, [canCompose])
93
35
 
94
36
  const openComposer = React.useCallback(() => {
95
- if (disabled || !composeEnabled) return
37
+ if (disabled) return
96
38
  setOpen(true)
97
- }, [disabled, composeEnabled])
39
+ }, [disabled])
98
40
  const contextObject = React.useMemo(() => ({
99
41
  entityModule: object.entityModule,
100
42
  entityType: object.entityType,
@@ -104,10 +46,6 @@ export function SendObjectMessageDialog({
104
46
  previewData: object.previewData ?? null,
105
47
  }), [object.entityId, object.entityModule, object.entityType, object.sourceEntityId, object.sourceEntityType, object.previewData])
106
48
 
107
- if (!composeAccessResolved || !composeEnabled) {
108
- return null
109
- }
110
-
111
49
  return (
112
50
  <>
113
51
  <Button
@@ -29,7 +29,6 @@ describe('SendObjectMessageDialog', () => {
29
29
  entityType: 'order',
30
30
  entityId: '11111111-1111-4111-8111-111111111111',
31
31
  }}
32
- canCompose
33
32
  />,
34
33
  { dict: {} },
35
34
  )
@@ -56,7 +55,6 @@ describe('SendObjectMessageDialog', () => {
56
55
  entityType: 'leave_request',
57
56
  entityId: '11111111-1111-4111-8111-111111111111',
58
57
  }}
59
- canCompose
60
58
  requiredActionConfig={{
61
59
  mode: 'optional',
62
60
  options: [{ id: 'approve', label: 'Approve' }],
@@ -76,20 +74,4 @@ describe('SendObjectMessageDialog', () => {
76
74
  open: true,
77
75
  }))
78
76
  })
79
-
80
- it('hides compose action when compose permission is disabled', async () => {
81
- renderWithProviders(
82
- <SendObjectMessageDialog
83
- object={{
84
- entityModule: 'sales',
85
- entityType: 'order',
86
- entityId: '11111111-1111-4111-8111-111111111111',
87
- }}
88
- canCompose={false}
89
- />,
90
- { dict: {} },
91
- )
92
-
93
- expect(screen.queryByRole('button', { name: 'Compose message' })).toBeNull()
94
- })
95
77
  })