@aglyn/plugins-inbox 1.0.0-beta.178 → 1.0.0-beta.180

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 (30) hide show
  1. package/package.json +10 -10
  2. package/src/lib/components/contacts-card.component.d.ts +10 -2
  3. package/src/lib/components/contacts-card.component.js +94 -41
  4. package/src/lib/components/contacts-card.component.js.map +1 -1
  5. package/src/lib/components/inbox-attribution-zone.d.ts +9 -1
  6. package/src/lib/components/inbox-attribution-zone.js +1 -1
  7. package/src/lib/components/inbox-attribution-zone.js.map +1 -1
  8. package/src/lib/components/inbox-console-page.d.ts +4 -0
  9. package/src/lib/components/inbox-console-page.js +119 -13
  10. package/src/lib/components/inbox-console-page.js.map +1 -1
  11. package/src/lib/components/inbox-console-sections.d.ts +13 -0
  12. package/src/lib/components/inbox-console-sections.js +18 -3
  13. package/src/lib/components/inbox-console-sections.js.map +1 -1
  14. package/src/lib/components/inbox-glance-card.component.js +6 -1
  15. package/src/lib/components/inbox-glance-card.component.js.map +1 -1
  16. package/src/lib/components/inbox-org-sites.d.ts +27 -0
  17. package/src/lib/components/inbox-org-sites.js +31 -0
  18. package/src/lib/components/inbox-org-sites.js.map +1 -0
  19. package/src/lib/components/submissions-card.component.d.ts +10 -2
  20. package/src/lib/components/submissions-card.component.js +89 -29
  21. package/src/lib/components/submissions-card.component.js.map +1 -1
  22. package/src/lib/components/use-inbox-site-pick.d.ts +38 -0
  23. package/src/lib/components/use-inbox-site-pick.js +99 -0
  24. package/src/lib/components/use-inbox-site-pick.js.map +1 -0
  25. package/src/lib/components/use-record-route-context.d.ts +8 -3
  26. package/src/lib/components/use-record-route-context.js +10 -6
  27. package/src/lib/components/use-record-route-context.js.map +1 -1
  28. package/src/lib/plugin.d.ts +4 -3
  29. package/src/lib/plugin.js +35 -5
  30. package/src/lib/plugin.js.map +1 -1
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { useCallback, useEffect, useMemo, useState } from "react";
18
+ const NO_HOSTS = [];
19
+ /**
20
+ * The session's memory of the pick, per org. Session storage, as the CRM's
21
+ * create-site pick is kept: the section rail is a set of routes, so the page
22
+ * mounts again on every section, and a filter that reset on each one would
23
+ * read as the rail having thrown the reader's choice away.
24
+ */ export const inboxSitePickKey = (orgId)=>`aglyn.inbox.site.${orgId}`;
25
+ function readPick(orgId) {
26
+ try {
27
+ return window.sessionStorage.getItem(inboxSitePickKey(orgId));
28
+ } catch (unused) {
29
+ return null;
30
+ }
31
+ }
32
+ function writePick(orgId, hostId) {
33
+ try {
34
+ if (hostId) window.sessionStorage.setItem(inboxSitePickKey(orgId), hostId);
35
+ else window.sessionStorage.removeItem(inboxSitePickKey(orgId));
36
+ } catch (unused) {
37
+ // A browser that refuses storage still gets the pick for this page.
38
+ }
39
+ }
40
+ /**
41
+ * The org Inbox's site filter. Handed no mount — under a site — it answers
42
+ * no pick and is never ready, and the page reads its own `hostId` instead.
43
+ */ export function useInboxSitePick(mount) {
44
+ var _ref, _ref1;
45
+ const orgId = (_ref = mount == null ? void 0 : mount.orgId) != null ? _ref : null;
46
+ const hosts = (_ref1 = mount == null ? void 0 : mount.hosts) != null ? _ref1 : NO_HOSTS;
47
+ const hostsReady = Boolean(mount == null ? void 0 : mount.hostsReady);
48
+ /*
49
+ * Read after mount, not in the initializer, so the server and the first
50
+ * client paint agree. Held with the org it was read for, so a switch of
51
+ * workspace never applies one org's pick to another's sites.
52
+ */ const [memory, setMemory] = useState(null);
53
+ useEffect(()=>{
54
+ if (orgId) setMemory({
55
+ orgId,
56
+ hostId: readPick(orgId)
57
+ });
58
+ }, [
59
+ orgId
60
+ ]);
61
+ const loaded = Boolean(orgId) && (memory == null ? void 0 : memory.orgId) === orgId;
62
+ const hostId = useMemo(()=>{
63
+ if (!hostsReady) return null;
64
+ if (hosts.length === 1) return hosts[0].id;
65
+ const remembered = loaded ? memory == null ? void 0 : memory.hostId : null;
66
+ // A remembered site the list no longer carries is forgotten, not read.
67
+ return remembered && hosts.some((host)=>host.id === remembered) ? remembered : null;
68
+ }, [
69
+ hostsReady,
70
+ hosts,
71
+ loaded,
72
+ memory
73
+ ]);
74
+ const options = useMemo(()=>hosts.length > 1 ? hosts.map((host)=>({
75
+ value: host.id,
76
+ label: host.name || host.subdomain || host.id
77
+ })) : [], [
78
+ hosts
79
+ ]);
80
+ const setHostId = useCallback((next)=>{
81
+ if (!orgId) return;
82
+ setMemory({
83
+ orgId,
84
+ hostId: next
85
+ });
86
+ writePick(orgId, next);
87
+ }, [
88
+ orgId
89
+ ]);
90
+ return {
91
+ hostId,
92
+ ready: loaded && hostsReady,
93
+ options,
94
+ setHostId
95
+ };
96
+ }
97
+ export default useInboxSitePick;
98
+
99
+ //# sourceMappingURL=use-inbox-site-pick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/inbox/src/lib/components/use-inbox-site-pick.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\n\n/**\n * The site the organization's Inbox is narrowed to (AGL-3303).\n *\n * `hostId` is `null` for every site at once — the default — or the one site\n * the reader picked, whose own view the page then draws: its submissions\n * with their form filter, its members beside its leads, its campaigns and\n * its ceiling notices. An organization with ONE site has nothing to choose\n * between, so that site is the pick and no picker is offered.\n */\nexport interface InboxSitePick {\n hostId: string | null\n /**\n * False until the remembered pick has been read and the org's sites have\n * settled. A section drawn before then would open the every-site reads for\n * a page about to switch to one site's, and bill both.\n */\n ready: boolean\n /** The sites to choose between, by name — empty when there is one or none. */\n options: ReadonlyArray<{ value: string; label: string }>\n setHostId: (hostId: string | null) => void\n}\n\nconst NO_HOSTS: ConsolePluginOrgMount['hosts'] = []\n\n/**\n * The session's memory of the pick, per org. Session storage, as the CRM's\n * create-site pick is kept: the section rail is a set of routes, so the page\n * mounts again on every section, and a filter that reset on each one would\n * read as the rail having thrown the reader's choice away.\n */\nexport const inboxSitePickKey = (orgId: string) => `aglyn.inbox.site.${orgId}`\n\nfunction readPick(orgId: string): string | null {\n try {\n return window.sessionStorage.getItem(inboxSitePickKey(orgId))\n } catch {\n return null\n }\n}\n\nfunction writePick(orgId: string, hostId: string | null): void {\n try {\n if (hostId) window.sessionStorage.setItem(inboxSitePickKey(orgId), hostId)\n else window.sessionStorage.removeItem(inboxSitePickKey(orgId))\n } catch {\n // A browser that refuses storage still gets the pick for this page.\n }\n}\n\n/**\n * The org Inbox's site filter. Handed no mount — under a site — it answers\n * no pick and is never ready, and the page reads its own `hostId` instead.\n */\nexport function useInboxSitePick(\n mount: ConsolePluginOrgMount | undefined,\n): InboxSitePick {\n const orgId = mount?.orgId ?? null\n const hosts = mount?.hosts ?? NO_HOSTS\n const hostsReady = Boolean(mount?.hostsReady)\n /*\n * Read after mount, not in the initializer, so the server and the first\n * client paint agree. Held with the org it was read for, so a switch of\n * workspace never applies one org's pick to another's sites.\n */\n const [memory, setMemory] = useState<{\n orgId: string\n hostId: string | null\n } | null>(null)\n useEffect(() => {\n if (orgId) setMemory({ orgId, hostId: readPick(orgId) })\n }, [orgId])\n const loaded = Boolean(orgId) && memory?.orgId === orgId\n\n const hostId = useMemo(() => {\n if (!hostsReady) return null\n if (hosts.length === 1) return hosts[0].id\n const remembered = loaded ? memory?.hostId : null\n // A remembered site the list no longer carries is forgotten, not read.\n return remembered && hosts.some((host) => host.id === remembered)\n ? remembered\n : null\n }, [hostsReady, hosts, loaded, memory])\n\n const options = useMemo(\n () =>\n hosts.length > 1\n ? hosts.map((host) => ({\n value: host.id,\n label: host.name || host.subdomain || host.id,\n }))\n : [],\n [hosts],\n )\n\n const setHostId = useCallback(\n (next: string | null) => {\n if (!orgId) return\n setMemory({ orgId, hostId: next })\n writePick(orgId, next)\n },\n [orgId],\n )\n\n return { hostId, ready: loaded && hostsReady, options, setHostId }\n}\n\nexport default useInboxSitePick\n"],"names":["useCallback","useEffect","useMemo","useState","NO_HOSTS","inboxSitePickKey","orgId","readPick","window","sessionStorage","getItem","writePick","hostId","setItem","removeItem","useInboxSitePick","mount","hosts","hostsReady","Boolean","memory","setMemory","loaded","length","id","remembered","some","host","options","map","value","label","name","subdomain","setHostId","next","ready"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAGA,SAASA,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAwBjE,MAAMC,WAA2C,EAAE;AAEnD;;;;;CAKC,GACD,OAAO,MAAMC,mBAAmB,CAACC,QAAkB,CAAC,iBAAiB,EAAEA,OAAO,CAAA;AAE9E,SAASC,SAASD,KAAa;IAC7B,IAAI;QACF,OAAOE,OAAOC,cAAc,CAACC,OAAO,CAACL,iBAAiBC;IACxD,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA,SAASK,UAAUL,KAAa,EAAEM,MAAqB;IACrD,IAAI;QACF,IAAIA,QAAQJ,OAAOC,cAAc,CAACI,OAAO,CAACR,iBAAiBC,QAAQM;aAC9DJ,OAAOC,cAAc,CAACK,UAAU,CAACT,iBAAiBC;IACzD,EAAE,eAAM;IACN,oEAAoE;IACtE;AACF;AAEA;;;CAGC,GACD,OAAO,SAASS,iBACdC,KAAwC;;IAExC,MAAMV,gBAAQU,yBAAAA,MAAOV,KAAK,mBAAI;IAC9B,MAAMW,iBAAQD,yBAAAA,MAAOC,KAAK,oBAAIb;IAC9B,MAAMc,aAAaC,QAAQH,yBAAAA,MAAOE,UAAU;IAC5C;;;;GAIC,GACD,MAAM,CAACE,QAAQC,UAAU,GAAGlB,SAGlB;IACVF,UAAU;QACR,IAAIK,OAAOe,UAAU;YAAEf;YAAOM,QAAQL,SAASD;QAAO;IACxD,GAAG;QAACA;KAAM;IACV,MAAMgB,SAASH,QAAQb,UAAUc,CAAAA,0BAAAA,OAAQd,KAAK,MAAKA;IAEnD,MAAMM,SAASV,QAAQ;QACrB,IAAI,CAACgB,YAAY,OAAO;QACxB,IAAID,MAAMM,MAAM,KAAK,GAAG,OAAON,KAAK,CAAC,EAAE,CAACO,EAAE;QAC1C,MAAMC,aAAaH,SAASF,0BAAAA,OAAQR,MAAM,GAAG;QAC7C,uEAAuE;QACvE,OAAOa,cAAcR,MAAMS,IAAI,CAAC,CAACC,OAASA,KAAKH,EAAE,KAAKC,cAClDA,aACA;IACN,GAAG;QAACP;QAAYD;QAAOK;QAAQF;KAAO;IAEtC,MAAMQ,UAAU1B,QACd,IACEe,MAAMM,MAAM,GAAG,IACXN,MAAMY,GAAG,CAAC,CAACF,OAAU,CAAA;gBACnBG,OAAOH,KAAKH,EAAE;gBACdO,OAAOJ,KAAKK,IAAI,IAAIL,KAAKM,SAAS,IAAIN,KAAKH,EAAE;YAC/C,CAAA,KACA,EAAE,EACR;QAACP;KAAM;IAGT,MAAMiB,YAAYlC,YAChB,CAACmC;QACC,IAAI,CAAC7B,OAAO;QACZe,UAAU;YAAEf;YAAOM,QAAQuB;QAAK;QAChCxB,UAAUL,OAAO6B;IACnB,GACA;QAAC7B;KAAM;IAGT,OAAO;QAAEM;QAAQwB,OAAOd,UAAUJ;QAAYU;QAASM;IAAU;AACnE;AAEA,eAAenB,iBAAgB"}
@@ -1,8 +1,8 @@
1
1
  import type { PluginRecordRouteContext } from '@aglyn/aglyn/plugin-manager/plugin-record-routes';
2
2
  /**
3
- * Where this console is — the organization and the site — for asking the
4
- * record-route registry for a link, or `null` until the route params settle
5
- * (AGL-2608).
3
+ * Where this console is — the organization, and the site when there is one —
4
+ * for asking the record-route registry for a link, or `null` until the route
5
+ * params settle (AGL-2608).
6
6
  *
7
7
  * Both are already in the URL, so no document is read to build a link. What
8
8
  * the Inbox does NOT know is the address itself: the plugin that keeps people
@@ -10,6 +10,11 @@ import type { PluginRecordRouteContext } from '@aglyn/aglyn/plugin-manager/plugi
10
10
  * `pluginRecordHref('lead', context, id)`. It lists leads; wherever they are
11
11
  * worked is that plugin's to say, and a workspace without one gets text
12
12
  * instead of a link.
13
+ *
14
+ * On the organization's Inbox the URL names no site, and `host: null` asks
15
+ * for the organization's address (AGL-3303) — the registry's own contract
16
+ * for a surface with no site, which a route answers with `null` for any
17
+ * kind it has no org-level page for.
13
18
  */
14
19
  export declare function useRecordRouteContext(): PluginRecordRouteContext | null;
15
20
  export default useRecordRouteContext;
@@ -16,9 +16,9 @@
16
16
  */ 'use client';
17
17
  import { useParams } from "next/navigation";
18
18
  /**
19
- * Where this console is — the organization and the site — for asking the
20
- * record-route registry for a link, or `null` until the route params settle
21
- * (AGL-2608).
19
+ * Where this console is — the organization, and the site when there is one —
20
+ * for asking the record-route registry for a link, or `null` until the route
21
+ * params settle (AGL-2608).
22
22
  *
23
23
  * Both are already in the URL, so no document is read to build a link. What
24
24
  * the Inbox does NOT know is the address itself: the plugin that keeps people
@@ -26,14 +26,18 @@ import { useParams } from "next/navigation";
26
26
  * `pluginRecordHref('lead', context, id)`. It lists leads; wherever they are
27
27
  * worked is that plugin's to say, and a workspace without one gets text
28
28
  * instead of a link.
29
+ *
30
+ * On the organization's Inbox the URL names no site, and `host: null` asks
31
+ * for the organization's address (AGL-3303) — the registry's own contract
32
+ * for a surface with no site, which a route answers with `null` for any
33
+ * kind it has no org-level page for.
29
34
  */ export function useRecordRouteContext() {
30
35
  const params = useParams();
31
36
  const orgSlug = params == null ? void 0 : params.orgSlug;
32
- const host = params == null ? void 0 : params.host;
33
- if (!orgSlug || !host) return null;
37
+ if (!orgSlug) return null;
34
38
  return {
35
39
  orgSlug,
36
- host
40
+ host: (params == null ? void 0 : params.host) || null
37
41
  };
38
42
  }
39
43
  export default useRecordRouteContext;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/inbox/src/lib/components/use-record-route-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { PluginRecordRouteContext } from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { useParams } from 'next/navigation'\n\n/**\n * Where this console is — the organization and the site — for asking the\n * record-route registry for a link, or `null` until the route params settle\n * (AGL-2608).\n *\n * Both are already in the URL, so no document is read to build a link. What\n * the Inbox does NOT know is the address itself: the plugin that keeps people\n * publishes where a lead or a contact is read, and the Inbox asks\n * `pluginRecordHref('lead', context, id)`. It lists leads; wherever they are\n * worked is that plugin's to say, and a workspace without one gets text\n * instead of a link.\n */\nexport function useRecordRouteContext(): PluginRecordRouteContext | null {\n const params = useParams<{ orgSlug: string; host: string }>()\n const orgSlug = params?.orgSlug\n const host = params?.host\n if (!orgSlug || !host) return null\n return { orgSlug, host }\n}\n\nexport default useRecordRouteContext\n"],"names":["useParams","useRecordRouteContext","params","orgSlug","host"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAGA,SAASA,SAAS,QAAQ,kBAAiB;AAE3C;;;;;;;;;;;CAWC,GACD,OAAO,SAASC;IACd,MAAMC,SAASF;IACf,MAAMG,UAAUD,0BAAAA,OAAQC,OAAO;IAC/B,MAAMC,OAAOF,0BAAAA,OAAQE,IAAI;IACzB,IAAI,CAACD,WAAW,CAACC,MAAM,OAAO;IAC9B,OAAO;QAAED;QAASC;IAAK;AACzB;AAEA,eAAeH,sBAAqB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/inbox/src/lib/components/use-record-route-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { PluginRecordRouteContext } from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { useParams } from 'next/navigation'\n\n/**\n * Where this console is — the organization, and the site when there is one —\n * for asking the record-route registry for a link, or `null` until the route\n * params settle (AGL-2608).\n *\n * Both are already in the URL, so no document is read to build a link. What\n * the Inbox does NOT know is the address itself: the plugin that keeps people\n * publishes where a lead or a contact is read, and the Inbox asks\n * `pluginRecordHref('lead', context, id)`. It lists leads; wherever they are\n * worked is that plugin's to say, and a workspace without one gets text\n * instead of a link.\n *\n * On the organization's Inbox the URL names no site, and `host: null` asks\n * for the organization's address (AGL-3303) — the registry's own contract\n * for a surface with no site, which a route answers with `null` for any\n * kind it has no org-level page for.\n */\nexport function useRecordRouteContext(): PluginRecordRouteContext | null {\n const params = useParams<{ orgSlug: string; host: string }>()\n const orgSlug = params?.orgSlug\n if (!orgSlug) return null\n return { orgSlug, host: params?.host || null }\n}\n\nexport default useRecordRouteContext\n"],"names":["useParams","useRecordRouteContext","params","orgSlug","host"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAGA,SAASA,SAAS,QAAQ,kBAAiB;AAE3C;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASC;IACd,MAAMC,SAASF;IACf,MAAMG,UAAUD,0BAAAA,OAAQC,OAAO;IAC/B,IAAI,CAACA,SAAS,OAAO;IACrB,OAAO;QAAEA;QAASC,MAAMF,CAAAA,0BAAAA,OAAQE,IAAI,KAAI;IAAK;AAC/C;AAEA,eAAeH,sBAAqB"}
@@ -18,8 +18,9 @@
18
18
  * Inbox feature plugin (AGL-395). Console-only — form submissions, site
19
19
  * members/leads and campaigns live in Firestore and have no canvas element,
20
20
  * so there is no UI bundle. The console half declares the Inbox nav + its
21
- * three sections through the ConsoleExtension registry (always-on). The
22
- * Campaigns section and the attribution shown inside a submission are zones
23
- * this plugin hosts; the plugin that owns campaigns draws in them.
21
+ * three sections through the ConsoleExtension registry (always-on), once
22
+ * under each site and once for the organization (AGL-3303). The Campaigns
23
+ * section and the attribution shown inside a submission are zones this
24
+ * plugin hosts; the plugin that owns campaigns draws in them.
24
25
  */
25
26
  export declare function registerInboxConsole(): void;
package/src/lib/plugin.js CHANGED
@@ -16,7 +16,7 @@
16
16
  */ import * as Aglyn from "@aglyn/aglyn";
17
17
  import { mdiInboxArrowDown } from "@aglyn/shared-data-mdi";
18
18
  import { lazy } from "react";
19
- import { INBOX_CONSOLE_SECTIONS } from "./components/inbox-console-sections.js";
19
+ import { INBOX_CONSOLE_SECTIONS, INBOX_ORG_CONSOLE_SECTIONS } from "./components/inbox-console-sections.js";
20
20
  import { registerPluginZone } from "@aglyn/aglyn/plugin-manager/plugin-zones";
21
21
  import { INBOX_CAMPAIGNS_ZONE, INBOX_RECORD_ATTRIBUTION_ZONE } from "./components/inbox-attribution-zone.js";
22
22
  import { BUNDLE_ID } from "./constants/bundle-common.js";
@@ -30,9 +30,10 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
30
30
  * Inbox feature plugin (AGL-395). Console-only — form submissions, site
31
31
  * members/leads and campaigns live in Firestore and have no canvas element,
32
32
  * so there is no UI bundle. The console half declares the Inbox nav + its
33
- * three sections through the ConsoleExtension registry (always-on). The
34
- * Campaigns section and the attribution shown inside a submission are zones
35
- * this plugin hosts; the plugin that owns campaigns draws in them.
33
+ * three sections through the ConsoleExtension registry (always-on), once
34
+ * under each site and once for the organization (AGL-3303). The Campaigns
35
+ * section and the attribution shown inside a submission are zones this
36
+ * plugin hosts; the plugin that owns campaigns draws in them.
36
37
  */ export function registerInboxConsole() {
37
38
  registerPluginZone({
38
39
  zone: INBOX_RECORD_ATTRIBUTION_ZONE,
@@ -47,7 +48,7 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
47
48
  zone: INBOX_CAMPAIGNS_ZONE,
48
49
  label: 'The Inbox’s Campaigns section',
49
50
  surface: 'console',
50
- description: 'The body of the Inbox’s Campaigns tab. A widget here lists the site’s campaigns; it is handed the site and nothing else.'
51
+ description: 'The body of the Inbox’s Campaigns tab. A widget here lists the site’s campaigns; it is handed the site and nothing else — or, on the organization’s Inbox, no site (`hostId: null`) and the organization with its sites (`orgMount`), and lists every site’s.'
51
52
  }, {
52
53
  pluginId: BUNDLE_ID
53
54
  });
@@ -95,6 +96,35 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
95
96
  },
96
97
  Component: InboxConsolePage
97
98
  }
99
+ ],
100
+ /*
101
+ * The ORGANIZATION's Inbox, at `/[orgSlug]/inbox` (AGL-3303): every
102
+ * site's submissions in one list, the organization's leads, and every
103
+ * campaign, with a site filter that narrows the page to one site's own
104
+ * view. The page is the same component; handed no site, it draws the
105
+ * org sections.
106
+ *
107
+ * It carries the SITE tab's id on purpose, as the org CRM and Marketing
108
+ * tabs do: `release_inbox` names `nav-tab-inbox`, so one flag holds both
109
+ * levels and the org Inbox cannot ship while the site's is switched off.
110
+ */ orgNavItems: [
111
+ {
112
+ label: 'Inbox',
113
+ href: '/inbox',
114
+ sections: INBOX_ORG_CONSOLE_SECTIONS,
115
+ navTabId: 'nav-tab-inbox',
116
+ icon: {
117
+ path: mdiInboxArrowDown.path
118
+ },
119
+ header: {
120
+ title: 'Inbox',
121
+ icon: {
122
+ path: mdiInboxArrowDown.path
123
+ },
124
+ docsTopic: 'forms'
125
+ },
126
+ Component: InboxConsolePage
127
+ }
98
128
  ]
99
129
  });
100
130
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/inbox/src/lib/plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiInboxArrowDown } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nimport { INBOX_CONSOLE_SECTIONS } from './components/inbox-console-sections'\nimport { registerPluginZone } from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport {\n INBOX_CAMPAIGNS_ZONE,\n INBOX_RECORD_ATTRIBUTION_ZONE,\n} from './components/inbox-attribution-zone'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/** Code-split: the Inbox console page only loads when opened. */\nconst InboxConsolePage = lazy(() => import('./components/inbox-console-page'))\n\n/**\n * Code-split: the submissions table loads when a form page asks for it. The\n * zone hands `hostId` and `formId`, which is what the table's own props are.\n */\nconst FormSubmissionsReader = lazy(\n () => import('./components/submissions-card.component'),\n)\n\n/** Dashboard glance card, loaded only where the shell renders the slot. */\nconst InboxGlanceCard = lazy(\n () => import('./components/inbox-glance-card.component'),\n)\n\n/**\n * Inbox feature plugin (AGL-395). Console-only — form submissions, site\n * members/leads and campaigns live in Firestore and have no canvas element,\n * so there is no UI bundle. The console half declares the Inbox nav + its\n * three sections through the ConsoleExtension registry (always-on). The\n * Campaigns section and the attribution shown inside a submission are zones\n * this plugin hosts; the plugin that owns campaigns draws in them.\n */\nexport function registerInboxConsole(): void {\n registerPluginZone(\n {\n zone: INBOX_RECORD_ATTRIBUTION_ZONE,\n label: 'Where a lead or a submission came from',\n surface: 'console',\n description:\n 'In a lead’s “Where this came from” dialog and under an open submission. A widget here says which campaign or link brought it; it is handed the site, what the record is and its id, and it writes nothing.',\n },\n // Named, because a spec calls this registrar without the loader.\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: INBOX_CAMPAIGNS_ZONE,\n label: 'The Inbox’s Campaigns section',\n surface: 'console',\n description:\n 'The body of the Inbox’s Campaigns tab. A widget here lists the site’s campaigns; it is handed the site and nothing else.',\n },\n { pluginId: BUNDLE_ID },\n )\n Aglyn.registerConsoleExtension({\n pluginId: BUNDLE_ID,\n displayName: 'Inbox',\n // The host dashboard's inbox glance. A form submission is the one thing\n // on a site that is waiting for a REPLY, and until this it was two\n // clicks from the page an owner opens first.\n widgets: [\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.hostDashboard,\n widgetId: 'inbox-glance',\n title: 'Inbox',\n Component: InboxGlanceCard,\n },\n // One form's submissions, on the form's own page. The forms plugin\n // hosts the zone and hands it the site and the form; the reader is this\n // plugin's table, narrowed to that form, so the form page gets the same\n // ordered, paged walk the Inbox uses without importing it.\n {\n slot: 'formSubmissions',\n widgetId: 'inbox-form-submissions',\n title: 'Submissions',\n Component: FormSubmissionsReader,\n },\n ],\n navItems: [\n {\n label: 'Inbox',\n href: '/inbox',\n // Sections as ROUTES (AGL-2501): each is a real URL the shell\n // resolves and gates, so the page mounts the one being read.\n sections: INBOX_CONSOLE_SECTIONS,\n navTabId: 'nav-tab-inbox',\n icon: { path: mdiInboxArrowDown.path },\n header: {\n title: 'Inbox',\n icon: { path: mdiInboxArrowDown.path },\n docsTopic: 'forms',\n },\n Component: InboxConsolePage,\n },\n ],\n })\n}\n"],"names":["Aglyn","mdiInboxArrowDown","lazy","INBOX_CONSOLE_SECTIONS","registerPluginZone","INBOX_CAMPAIGNS_ZONE","INBOX_RECORD_ATTRIBUTION_ZONE","BUNDLE_ID","InboxConsolePage","FormSubmissionsReader","InboxGlanceCard","registerInboxConsole","zone","label","surface","description","pluginId","registerConsoleExtension","displayName","widgets","slot","CONSOLE_WIDGET_SLOTS","hostDashboard","widgetId","title","Component","navItems","href","sections","navTabId","icon","path","header","docsTopic"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,IAAI,QAAQ,QAAO;AAC5B,SAASC,sBAAsB,QAAQ,yCAAqC;AAC5E,SAASC,kBAAkB,QAAQ,2CAA0C;AAC7E,SACEC,oBAAoB,EACpBC,6BAA6B,QACxB,yCAAqC;AAC5C,SAASC,SAAS,QAAQ,+BAA2B;AAErD,+DAA+D,GAC/D,MAAMC,mBAAmBN,KAAK,IAAM,MAAM,CAAC;AAE3C;;;CAGC,GACD,MAAMO,wBAAwBP,KAC5B,IAAM,MAAM,CAAC;AAGf,yEAAyE,GACzE,MAAMQ,kBAAkBR,KACtB,IAAM,MAAM,CAAC;AAGf;;;;;;;CAOC,GACD,OAAO,SAASS;IACdP,mBACE;QACEQ,MAAMN;QACNO,OAAO;QACPC,SAAS;QACTC,aACE;IACJ,GACA,iEAAiE;IACjE;QAAEC,UAAUT;IAAU;IAExBH,mBACE;QACEQ,MAAMP;QACNQ,OAAO;QACPC,SAAS;QACTC,aACE;IACJ,GACA;QAAEC,UAAUT;IAAU;IAExBP,MAAMiB,wBAAwB,CAAC;QAC7BD,UAAUT;QACVW,aAAa;QACb,wEAAwE;QACxE,mEAAmE;QACnE,6CAA6C;QAC7CC,SAAS;YACP;gBACEC,MAAMpB,MAAMqB,oBAAoB,CAACC,aAAa;gBAC9CC,UAAU;gBACVC,OAAO;gBACPC,WAAWf;YACb;YACA,mEAAmE;YACnE,wEAAwE;YACxE,wEAAwE;YACxE,2DAA2D;YAC3D;gBACEU,MAAM;gBACNG,UAAU;gBACVC,OAAO;gBACPC,WAAWhB;YACb;SACD;QACDiB,UAAU;YACR;gBACEb,OAAO;gBACPc,MAAM;gBACN,8DAA8D;gBAC9D,6DAA6D;gBAC7DC,UAAUzB;gBACV0B,UAAU;gBACVC,MAAM;oBAAEC,MAAM9B,kBAAkB8B,IAAI;gBAAC;gBACrCC,QAAQ;oBACNR,OAAO;oBACPM,MAAM;wBAAEC,MAAM9B,kBAAkB8B,IAAI;oBAAC;oBACrCE,WAAW;gBACb;gBACAR,WAAWjB;YACb;SACD;IACH;AACF"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/inbox/src/lib/plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiInboxArrowDown } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nimport {\n INBOX_CONSOLE_SECTIONS,\n INBOX_ORG_CONSOLE_SECTIONS,\n} from './components/inbox-console-sections'\nimport { registerPluginZone } from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport {\n INBOX_CAMPAIGNS_ZONE,\n INBOX_RECORD_ATTRIBUTION_ZONE,\n} from './components/inbox-attribution-zone'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/** Code-split: the Inbox console page only loads when opened. */\nconst InboxConsolePage = lazy(() => import('./components/inbox-console-page'))\n\n/**\n * Code-split: the submissions table loads when a form page asks for it. The\n * zone hands `hostId` and `formId`, which is what the table's own props are.\n */\nconst FormSubmissionsReader = lazy(\n () => import('./components/submissions-card.component'),\n)\n\n/** Dashboard glance card, loaded only where the shell renders the slot. */\nconst InboxGlanceCard = lazy(\n () => import('./components/inbox-glance-card.component'),\n)\n\n/**\n * Inbox feature plugin (AGL-395). Console-only — form submissions, site\n * members/leads and campaigns live in Firestore and have no canvas element,\n * so there is no UI bundle. The console half declares the Inbox nav + its\n * three sections through the ConsoleExtension registry (always-on), once\n * under each site and once for the organization (AGL-3303). The Campaigns\n * section and the attribution shown inside a submission are zones this\n * plugin hosts; the plugin that owns campaigns draws in them.\n */\nexport function registerInboxConsole(): void {\n registerPluginZone(\n {\n zone: INBOX_RECORD_ATTRIBUTION_ZONE,\n label: 'Where a lead or a submission came from',\n surface: 'console',\n description:\n 'In a lead’s “Where this came from” dialog and under an open submission. A widget here says which campaign or link brought it; it is handed the site, what the record is and its id, and it writes nothing.',\n },\n // Named, because a spec calls this registrar without the loader.\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: INBOX_CAMPAIGNS_ZONE,\n label: 'The Inbox’s Campaigns section',\n surface: 'console',\n description:\n 'The body of the Inbox’s Campaigns tab. A widget here lists the site’s campaigns; it is handed the site and nothing else — or, on the organization’s Inbox, no site (`hostId: null`) and the organization with its sites (`orgMount`), and lists every site’s.',\n },\n { pluginId: BUNDLE_ID },\n )\n Aglyn.registerConsoleExtension({\n pluginId: BUNDLE_ID,\n displayName: 'Inbox',\n // The host dashboard's inbox glance. A form submission is the one thing\n // on a site that is waiting for a REPLY, and until this it was two\n // clicks from the page an owner opens first.\n widgets: [\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.hostDashboard,\n widgetId: 'inbox-glance',\n title: 'Inbox',\n Component: InboxGlanceCard,\n },\n // One form's submissions, on the form's own page. The forms plugin\n // hosts the zone and hands it the site and the form; the reader is this\n // plugin's table, narrowed to that form, so the form page gets the same\n // ordered, paged walk the Inbox uses without importing it.\n {\n slot: 'formSubmissions',\n widgetId: 'inbox-form-submissions',\n title: 'Submissions',\n Component: FormSubmissionsReader,\n },\n ],\n navItems: [\n {\n label: 'Inbox',\n href: '/inbox',\n // Sections as ROUTES (AGL-2501): each is a real URL the shell\n // resolves and gates, so the page mounts the one being read.\n sections: INBOX_CONSOLE_SECTIONS,\n navTabId: 'nav-tab-inbox',\n icon: { path: mdiInboxArrowDown.path },\n header: {\n title: 'Inbox',\n icon: { path: mdiInboxArrowDown.path },\n docsTopic: 'forms',\n },\n Component: InboxConsolePage,\n },\n ],\n /*\n * The ORGANIZATION's Inbox, at `/[orgSlug]/inbox` (AGL-3303): every\n * site's submissions in one list, the organization's leads, and every\n * campaign, with a site filter that narrows the page to one site's own\n * view. The page is the same component; handed no site, it draws the\n * org sections.\n *\n * It carries the SITE tab's id on purpose, as the org CRM and Marketing\n * tabs do: `release_inbox` names `nav-tab-inbox`, so one flag holds both\n * levels and the org Inbox cannot ship while the site's is switched off.\n */\n orgNavItems: [\n {\n label: 'Inbox',\n href: '/inbox',\n sections: INBOX_ORG_CONSOLE_SECTIONS,\n navTabId: 'nav-tab-inbox',\n icon: { path: mdiInboxArrowDown.path },\n header: {\n title: 'Inbox',\n icon: { path: mdiInboxArrowDown.path },\n docsTopic: 'forms',\n },\n Component: InboxConsolePage,\n },\n ],\n })\n}\n"],"names":["Aglyn","mdiInboxArrowDown","lazy","INBOX_CONSOLE_SECTIONS","INBOX_ORG_CONSOLE_SECTIONS","registerPluginZone","INBOX_CAMPAIGNS_ZONE","INBOX_RECORD_ATTRIBUTION_ZONE","BUNDLE_ID","InboxConsolePage","FormSubmissionsReader","InboxGlanceCard","registerInboxConsole","zone","label","surface","description","pluginId","registerConsoleExtension","displayName","widgets","slot","CONSOLE_WIDGET_SLOTS","hostDashboard","widgetId","title","Component","navItems","href","sections","navTabId","icon","path","header","docsTopic","orgNavItems"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,IAAI,QAAQ,QAAO;AAC5B,SACEC,sBAAsB,EACtBC,0BAA0B,QACrB,yCAAqC;AAC5C,SAASC,kBAAkB,QAAQ,2CAA0C;AAC7E,SACEC,oBAAoB,EACpBC,6BAA6B,QACxB,yCAAqC;AAC5C,SAASC,SAAS,QAAQ,+BAA2B;AAErD,+DAA+D,GAC/D,MAAMC,mBAAmBP,KAAK,IAAM,MAAM,CAAC;AAE3C;;;CAGC,GACD,MAAMQ,wBAAwBR,KAC5B,IAAM,MAAM,CAAC;AAGf,yEAAyE,GACzE,MAAMS,kBAAkBT,KACtB,IAAM,MAAM,CAAC;AAGf;;;;;;;;CAQC,GACD,OAAO,SAASU;IACdP,mBACE;QACEQ,MAAMN;QACNO,OAAO;QACPC,SAAS;QACTC,aACE;IACJ,GACA,iEAAiE;IACjE;QAAEC,UAAUT;IAAU;IAExBH,mBACE;QACEQ,MAAMP;QACNQ,OAAO;QACPC,SAAS;QACTC,aACE;IACJ,GACA;QAAEC,UAAUT;IAAU;IAExBR,MAAMkB,wBAAwB,CAAC;QAC7BD,UAAUT;QACVW,aAAa;QACb,wEAAwE;QACxE,mEAAmE;QACnE,6CAA6C;QAC7CC,SAAS;YACP;gBACEC,MAAMrB,MAAMsB,oBAAoB,CAACC,aAAa;gBAC9CC,UAAU;gBACVC,OAAO;gBACPC,WAAWf;YACb;YACA,mEAAmE;YACnE,wEAAwE;YACxE,wEAAwE;YACxE,2DAA2D;YAC3D;gBACEU,MAAM;gBACNG,UAAU;gBACVC,OAAO;gBACPC,WAAWhB;YACb;SACD;QACDiB,UAAU;YACR;gBACEb,OAAO;gBACPc,MAAM;gBACN,8DAA8D;gBAC9D,6DAA6D;gBAC7DC,UAAU1B;gBACV2B,UAAU;gBACVC,MAAM;oBAAEC,MAAM/B,kBAAkB+B,IAAI;gBAAC;gBACrCC,QAAQ;oBACNR,OAAO;oBACPM,MAAM;wBAAEC,MAAM/B,kBAAkB+B,IAAI;oBAAC;oBACrCE,WAAW;gBACb;gBACAR,WAAWjB;YACb;SACD;QACD;;;;;;;;;;KAUC,GACD0B,aAAa;YACX;gBACErB,OAAO;gBACPc,MAAM;gBACNC,UAAUzB;gBACV0B,UAAU;gBACVC,MAAM;oBAAEC,MAAM/B,kBAAkB+B,IAAI;gBAAC;gBACrCC,QAAQ;oBACNR,OAAO;oBACPM,MAAM;wBAAEC,MAAM/B,kBAAkB+B,IAAI;oBAAC;oBACrCE,WAAW;gBACb;gBACAR,WAAWjB;YACb;SACD;IACH;AACF"}