@mercurjs/dashboard-shared 2.3.2-canary.1 → 2.3.2-canary.2

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.
package/dist/index.d.ts CHANGED
@@ -16,7 +16,8 @@ import { CellContext, Row, ColumnDef, ColumnDefTemplate, HeaderContext, Table as
16
16
  import { TFunction } from 'i18next';
17
17
  import { ClientError } from '@mercurjs/client';
18
18
  import * as react_currency_input_field from 'react-currency-input-field';
19
- import { CustomFieldsConfig, NavItemOverride, CustomFormField, CustomDisplayField, SectionAction, CustomListExtension } from '@mercurjs/dashboard-sdk';
19
+ import * as _mercurjs_dashboard_sdk from '@mercurjs/dashboard-sdk';
20
+ import { PermissionsContextValue, UserPolicy, PermissionsRequirementsContextValue, Permission, PermissionResource, PermissionOperation, CustomFieldsConfig, NavItemOverride, CustomFormField, CustomDisplayField, SectionAction, CustomListExtension } from '@mercurjs/dashboard-sdk';
20
21
 
21
22
  type TQueryKey<TKey, TListQuery = any, TDetailQuery = string> = {
22
23
  all: readonly [TKey];
@@ -1508,6 +1509,151 @@ type QueryParams<T extends string> = {
1508
1509
  };
1509
1510
  declare function useQueryParams<T extends string>(keys: T[], prefix?: string): QueryParams<T>;
1510
1511
 
1512
+ declare const PermissionsContext: React$1.Context<PermissionsContextValue | null>;
1513
+
1514
+ interface PermissionsProviderProps extends PropsWithChildren {
1515
+ policy: UserPolicy | null;
1516
+ isLoading?: boolean;
1517
+ /**
1518
+ * Whether the RBAC feature flag is enabled. When `false`, every permission
1519
+ * check resolves to `true`.
1520
+ */
1521
+ isRbacEnabled?: boolean;
1522
+ }
1523
+ declare const PermissionsProvider: ({ policy, isLoading, isRbacEnabled, children, }: PermissionsProviderProps) => react_jsx_runtime.JSX.Element;
1524
+
1525
+ declare const PermissionsRequirementsContext: React$1.Context<PermissionsRequirementsContextValue | null>;
1526
+
1527
+ /**
1528
+ * Collects the permission requirements declared by descendant guards so a
1529
+ * page can surface them (see `RequiredPermissionsSection`). Mount it around a
1530
+ * page body; guards work fine without it.
1531
+ */
1532
+ declare const PermissionsRequirementsProvider: ({ children, }: PropsWithChildren) => react_jsx_runtime.JSX.Element;
1533
+
1534
+ interface BasePermissionProps {
1535
+ source?: string;
1536
+ enabled?: boolean;
1537
+ }
1538
+ interface WithPermission extends BasePermissionProps {
1539
+ permission: Permission;
1540
+ permissions?: never;
1541
+ resource?: never;
1542
+ operation?: never;
1543
+ requireAll?: never;
1544
+ }
1545
+ interface WithPermissions extends BasePermissionProps {
1546
+ permissions: Permission[];
1547
+ /** If true, ALL permissions are required. Defaults to ANY. */
1548
+ requireAll?: boolean;
1549
+ permission?: never;
1550
+ resource?: never;
1551
+ operation?: never;
1552
+ }
1553
+ interface WithResourceOperation extends BasePermissionProps {
1554
+ resource: PermissionResource;
1555
+ operation: PermissionOperation;
1556
+ permission?: never;
1557
+ permissions?: never;
1558
+ requireAll?: never;
1559
+ }
1560
+ type PermissionProps = WithPermission | WithPermissions | WithResourceOperation;
1561
+ declare const resolvePermissionProps: (props: PermissionProps) => {
1562
+ permissions: Permission[] | null;
1563
+ requireAll: boolean;
1564
+ };
1565
+
1566
+ type PermissionsRequirementProps = PropsWithChildren<PermissionProps>;
1567
+ /**
1568
+ * Declares a permission requirement for the surrounding subtree without
1569
+ * gating it. Use `PermissionGuard` when the children should be hidden.
1570
+ */
1571
+ declare const PermissionsRequirement: ({ children, source, enabled, ...props }: PermissionsRequirementProps) => react_jsx_runtime.JSX.Element;
1572
+
1573
+ type PermissionGuardProps = PropsWithChildren<PermissionProps & {
1574
+ /** Rendered when access is denied. Nothing renders when omitted. */
1575
+ fallback?: ReactNode;
1576
+ showLoading?: boolean;
1577
+ loadingComponent?: ReactNode;
1578
+ }>;
1579
+ /**
1580
+ * Hides its children unless the actor holds the declared permission, and
1581
+ * registers the requirement so the page can surface what is missing.
1582
+ *
1583
+ * @example
1584
+ * ```tsx
1585
+ * <PermissionGuard resource="product" operation="create">
1586
+ * <Button>Create</Button>
1587
+ * </PermissionGuard>
1588
+ * ```
1589
+ */
1590
+ declare const PermissionGuard: ({ children, fallback, showLoading, loadingComponent, source, enabled, ...props }: PermissionGuardProps) => react_jsx_runtime.JSX.Element;
1591
+
1592
+ /**
1593
+ * Route-level guard. Mount as a route's `element` and declare the requirement
1594
+ * on the route's `handle`. Unlike `PermissionGuard`, `requireAll` defaults to
1595
+ * `true` here.
1596
+ *
1597
+ * @example
1598
+ * ```tsx
1599
+ * {
1600
+ * path: "roles",
1601
+ * element: <RoutePermissionGuard />,
1602
+ * handle: { permissions: "rbac_role:read" },
1603
+ * children: [...],
1604
+ * }
1605
+ * ```
1606
+ */
1607
+ declare const RoutePermissionGuard: () => react_jsx_runtime.JSX.Element;
1608
+
1609
+ /**
1610
+ * Renders the requirements that descendant guards registered with the nearest
1611
+ * `PermissionsRequirementsProvider`. Renders nothing when RBAC is off.
1612
+ */
1613
+ declare const RequiredPermissionsSection: () => react_jsx_runtime.JSX.Element | null;
1614
+
1615
+ /**
1616
+ * @example
1617
+ * ```tsx
1618
+ * const { can, hasPermission } = usePermissions()
1619
+ *
1620
+ * if (can("customer", "create")) { ... }
1621
+ * if (hasPermission("customer:read")) { ... }
1622
+ * ```
1623
+ */
1624
+ declare const usePermissions: () => _mercurjs_dashboard_sdk.PermissionsContextValue;
1625
+
1626
+ interface UseRegisterPermissionsOptions {
1627
+ /** If true, ALL permissions are required. Defaults to ANY. */
1628
+ requireAll?: boolean;
1629
+ source?: string;
1630
+ enabled?: boolean;
1631
+ }
1632
+ /**
1633
+ * Declares what the calling subtree needs. No-ops when there is no
1634
+ * `PermissionsRequirementsProvider` above it.
1635
+ */
1636
+ declare const useRegisterPermissions: (permissions: Permission[] | null | undefined, options?: UseRegisterPermissionsOptions) => void;
1637
+
1638
+ declare const useRequiredPermissions: () => _mercurjs_dashboard_sdk.PermissionRequirement[];
1639
+
1640
+ /**
1641
+ * @example
1642
+ * ```tsx
1643
+ * const { canCreate } = useResourcePermissions("product")
1644
+ * {canCreate && <Button>Create</Button>}
1645
+ * ```
1646
+ */
1647
+ declare const useResourcePermissions: (resource: PermissionResource) => {
1648
+ canRead: boolean;
1649
+ canCreate: boolean;
1650
+ canUpdate: boolean;
1651
+ canDelete: boolean;
1652
+ can: (operation: "read" | "create" | "update" | "delete") => boolean;
1653
+ resource: PermissionResource;
1654
+ isLoading: boolean;
1655
+ };
1656
+
1511
1657
  type WidgetPlacement = "before" | "after";
1512
1658
  type Widget = {
1513
1659
  Component: ComponentType<{
@@ -1900,4 +2046,4 @@ declare const extractPricesFromOffers: (offers: ExtractableOffers, regions: Http
1900
2046
  variant_id: string;
1901
2047
  }[];
1902
2048
 
1903
- export { type Action, type ActionGroup, ActionMenu, AddressForm, AddressSchema, type AttributeChange, type AttributeChangeKind, AttributeValueInput, BadgeListSummary, ChipGroup, ChipInput, CodeCell, CodeHeader, Combobox, type Command, ConditionalTooltip, ConfirmPrompt, type ConfirmPromptProps, type CoreNavItem, CountrySelect, CreatedAtCell, CreatedAtHeader, type CurrencyInfo, type CustomFieldsModule, CustomerInfo, DataGrid, DataTable, DateCell, DateHeader, DateRangeDisplay, DeprecatedPercentageInput, DisplayExtensionZone, type DisplayExtensionZoneProps, DisplayField, type DisplayFieldProps, DisplaySection, type DisplaySectionField, type DisplaySectionProps, type EditAttributeAttribute, EditAttributeForm, type EditAttributeFormProps, EmailCell, EmailForm, EmailHeader, EmailSchema, ErrorBoundary, type ExtendableTable, ExtensionProvider, type ExtensionProviderProps, ExtensionRegistry, type ExtractableOffer, type ExtractableOffers, type FieldDiff, FilePreview, type FileType, FileUpload, type FileUploadProps, type Filter, FilterGroup, Form, FormExtensionZone, type FormExtensionZoneProps, GeneralSectionSkeleton, HandleInput, HeadingSkeleton, IconAvatar, IconButtonSkeleton, ImageAvatar, type ImageRef, IncludesTaxTooltip, InfiniteList, JsonViewSection, JsonViewSectionSkeleton, KeyboundForm, LinkButton, ListBadge, ListSummary, Listicle, type ListicleProps, LocalizedTablePagination, type MediaDiff, MetadataForm, MetadataSection, MoneyAmountCell, NameCell, NameHeader, type NavigationModule, NoRecords, type NoRecordsProps, NoResults, type NoResultsProps, OrderBy, PercentageInput, PlaceholderCell, PriceListDateStatus, type PriceListGridGroupRow, type PriceListGridOfferRow, type PriceListGridRow, PriceListStatus, PriceListType, type ProductAttributeBatchPayload, ProductAttributeSection, type ProductAttributeSectionProps, type ProductChangeAttribute, ProductChangePanel, type ProductChangePanelProps, type ProductChangeProduct, type ProductChangeResolvers, type ProductChangeVariant, type ProductChangeView, ProgressBar, ProvinceSelect, Query, REFERENCE_FIELDS, type ReferenceField, type ResolvedAttribute, type ResolvedDisplays, type ResolvedFormField, RouteDrawer, RouteFocusModal, SectionRow, type SectionRowProps, SegmentedControl, type SegmentedControlOption, SidebarLink, type SidebarLinkProps, SingleColumnPage, SingleColumnPageSkeleton, Skeleton, SortableList, SortableTree, StackedDrawer, StackedFocusModal, type StaticCountry, StatusCell, SwitchBox, type TQueryKey, type TabDefinition, TabbedForm, TableFooterSkeleton, TableSectionSkeleton, TableSkeleton, TextCell, TextHeader, TextSkeleton, Thumbnail, TransferOwnershipSchema, TwoColumnPage, TwoColumnPageSkeleton, type UseExtendableFormProps, type UseExtendableTableProps, type UseQueryOptionsWrapper, type VariantGroup, VisuallyHidden, type Widget, type WidgetModule, type WidgetPlacement, WidgetZone, type WidgetZoneProps, type ZoneWidgets, _DataTable, applyNavOverrides, buildAdditionalDataDefaults, buildAdditionalDataSchema, buildOfferGridData, buildProductChangeView, countries, createDataGridHelper, createDataGridPriceColumns, createFormHelper, currencies, extractPricesFromOffers, extractReferenceIds, formatFieldValue, genericForwardRef, getCountryByIso2, getCurrencySymbol, getDecimalDigits, getExtensionRegistry, getFormattedAddress, getFormattedCountry, getLinkQuery, getLocaleAmount, getNativeSymbol, getPriceListStatus, getStylizedAmount, humanizeFieldName, isAmountLessThenRoundingError, isImageList, isPriceListGroupRow, isProductRow, isReferenceField, isSameAddress, linkFields, productChangeViewHasContent, queryClient, queryKeysFactory, useCombinedRefs, useCommandHistory, useDataTable, useDate, useDisplayFieldOverride, useDocumentDirection, useExtendableForm, useExtendableTable, useExtension, useLinkQuery, useQueryParams, useRouteModal, useStackedModal, useTabManagement, useTabbedForm, withLinkFields };
2049
+ export { type Action, type ActionGroup, ActionMenu, AddressForm, AddressSchema, type AttributeChange, type AttributeChangeKind, AttributeValueInput, BadgeListSummary, ChipGroup, ChipInput, CodeCell, CodeHeader, Combobox, type Command, ConditionalTooltip, ConfirmPrompt, type ConfirmPromptProps, type CoreNavItem, CountrySelect, CreatedAtCell, CreatedAtHeader, type CurrencyInfo, type CustomFieldsModule, CustomerInfo, DataGrid, DataTable, DateCell, DateHeader, DateRangeDisplay, DeprecatedPercentageInput, DisplayExtensionZone, type DisplayExtensionZoneProps, DisplayField, type DisplayFieldProps, DisplaySection, type DisplaySectionField, type DisplaySectionProps, type EditAttributeAttribute, EditAttributeForm, type EditAttributeFormProps, EmailCell, EmailForm, EmailHeader, EmailSchema, ErrorBoundary, type ExtendableTable, ExtensionProvider, type ExtensionProviderProps, ExtensionRegistry, type ExtractableOffer, type ExtractableOffers, type FieldDiff, FilePreview, type FileType, FileUpload, type FileUploadProps, type Filter, FilterGroup, Form, FormExtensionZone, type FormExtensionZoneProps, GeneralSectionSkeleton, HandleInput, HeadingSkeleton, IconAvatar, IconButtonSkeleton, ImageAvatar, type ImageRef, IncludesTaxTooltip, InfiniteList, JsonViewSection, JsonViewSectionSkeleton, KeyboundForm, LinkButton, ListBadge, ListSummary, Listicle, type ListicleProps, LocalizedTablePagination, type MediaDiff, MetadataForm, MetadataSection, MoneyAmountCell, NameCell, NameHeader, type NavigationModule, NoRecords, type NoRecordsProps, NoResults, type NoResultsProps, OrderBy, PercentageInput, PermissionGuard, type PermissionGuardProps, type PermissionProps, PermissionsContext, PermissionsProvider, type PermissionsProviderProps, PermissionsRequirement, type PermissionsRequirementProps, PermissionsRequirementsContext, PermissionsRequirementsProvider, PlaceholderCell, PriceListDateStatus, type PriceListGridGroupRow, type PriceListGridOfferRow, type PriceListGridRow, PriceListStatus, PriceListType, type ProductAttributeBatchPayload, ProductAttributeSection, type ProductAttributeSectionProps, type ProductChangeAttribute, ProductChangePanel, type ProductChangePanelProps, type ProductChangeProduct, type ProductChangeResolvers, type ProductChangeVariant, type ProductChangeView, ProgressBar, ProvinceSelect, Query, REFERENCE_FIELDS, type ReferenceField, RequiredPermissionsSection, type ResolvedAttribute, type ResolvedDisplays, type ResolvedFormField, RouteDrawer, RouteFocusModal, RoutePermissionGuard, SectionRow, type SectionRowProps, SegmentedControl, type SegmentedControlOption, SidebarLink, type SidebarLinkProps, SingleColumnPage, SingleColumnPageSkeleton, Skeleton, SortableList, SortableTree, StackedDrawer, StackedFocusModal, type StaticCountry, StatusCell, SwitchBox, type TQueryKey, type TabDefinition, TabbedForm, TableFooterSkeleton, TableSectionSkeleton, TableSkeleton, TextCell, TextHeader, TextSkeleton, Thumbnail, TransferOwnershipSchema, TwoColumnPage, TwoColumnPageSkeleton, type UseExtendableFormProps, type UseExtendableTableProps, type UseQueryOptionsWrapper, type UseRegisterPermissionsOptions, type VariantGroup, VisuallyHidden, type Widget, type WidgetModule, type WidgetPlacement, WidgetZone, type WidgetZoneProps, type ZoneWidgets, _DataTable, applyNavOverrides, buildAdditionalDataDefaults, buildAdditionalDataSchema, buildOfferGridData, buildProductChangeView, countries, createDataGridHelper, createDataGridPriceColumns, createFormHelper, currencies, extractPricesFromOffers, extractReferenceIds, formatFieldValue, genericForwardRef, getCountryByIso2, getCurrencySymbol, getDecimalDigits, getExtensionRegistry, getFormattedAddress, getFormattedCountry, getLinkQuery, getLocaleAmount, getNativeSymbol, getPriceListStatus, getStylizedAmount, humanizeFieldName, isAmountLessThenRoundingError, isImageList, isPriceListGroupRow, isProductRow, isReferenceField, isSameAddress, linkFields, productChangeViewHasContent, queryClient, queryKeysFactory, resolvePermissionProps, useCombinedRefs, useCommandHistory, useDataTable, useDate, useDisplayFieldOverride, useDocumentDirection, useExtendableForm, useExtendableTable, useExtension, useLinkQuery, usePermissions, useQueryParams, useRegisterPermissions, useRequiredPermissions, useResourcePermissions, useRouteModal, useStackedModal, useTabManagement, useTabbedForm, withLinkFields };
package/dist/index.js CHANGED
@@ -27882,6 +27882,433 @@ var useDataTable2 = ({
27882
27882
  return { table };
27883
27883
  };
27884
27884
 
27885
+ // src/permissions/permissions-context.tsx
27886
+ import { createContext as createContext10 } from "react";
27887
+ var PermissionsContext = createContext10(
27888
+ null
27889
+ );
27890
+
27891
+ // src/permissions/permissions-provider.tsx
27892
+ import { useCallback as useCallback26, useMemo as useMemo22 } from "react";
27893
+
27894
+ // ../dashboard-sdk/dist/index.js
27895
+ var OPERATION_IMPLICATIONS = {
27896
+ read: ["read"],
27897
+ create: ["create"],
27898
+ update: ["update"],
27899
+ delete: ["delete"],
27900
+ "*": ["read", "create", "update", "delete", "*"]
27901
+ };
27902
+ function parsePermission(permission) {
27903
+ const parts = permission.split(":");
27904
+ if (parts.length !== 2) {
27905
+ return null;
27906
+ }
27907
+ const [resource, operation] = parts;
27908
+ return {
27909
+ resource,
27910
+ operation
27911
+ };
27912
+ }
27913
+ function buildPermission(resource, operation) {
27914
+ return `${resource}:${operation}`;
27915
+ }
27916
+
27917
+ // src/permissions/permissions-provider.tsx
27918
+ import { jsx as jsx105 } from "react/jsx-runtime";
27919
+ var PermissionsProvider = ({
27920
+ policy,
27921
+ isLoading = false,
27922
+ isRbacEnabled = true,
27923
+ children
27924
+ }) => {
27925
+ const permissionsMap = useMemo22(() => {
27926
+ const index = /* @__PURE__ */ Object.create(null);
27927
+ for (const granted of policy?.permissions ?? []) {
27928
+ const parsed = parsePermission(granted);
27929
+ if (!parsed) {
27930
+ continue;
27931
+ }
27932
+ const { resource, operation } = parsed;
27933
+ const impliedOperations = OPERATION_IMPLICATIONS[operation] || [operation];
27934
+ for (const impliedOperation of impliedOperations) {
27935
+ index[buildPermission(resource, impliedOperation)] = true;
27936
+ }
27937
+ }
27938
+ return index;
27939
+ }, [policy]);
27940
+ const hasPermission = useCallback26(
27941
+ (permission) => {
27942
+ if (!isRbacEnabled) {
27943
+ return true;
27944
+ }
27945
+ return !!permissionsMap[permission];
27946
+ },
27947
+ [isRbacEnabled, permissionsMap]
27948
+ );
27949
+ const hasAnyPermission = useCallback26(
27950
+ (permissions) => {
27951
+ if (!isRbacEnabled) {
27952
+ return true;
27953
+ }
27954
+ if (!permissions?.length) {
27955
+ return false;
27956
+ }
27957
+ return permissions.some(hasPermission);
27958
+ },
27959
+ [isRbacEnabled, hasPermission]
27960
+ );
27961
+ const hasAllPermissions = useCallback26(
27962
+ (permissions) => {
27963
+ if (!isRbacEnabled) {
27964
+ return true;
27965
+ }
27966
+ if (!permissions?.length) {
27967
+ return false;
27968
+ }
27969
+ return permissions.every(hasPermission);
27970
+ },
27971
+ [isRbacEnabled, hasPermission]
27972
+ );
27973
+ const can = useCallback26(
27974
+ (resource, operation) => {
27975
+ if (!isRbacEnabled) {
27976
+ return true;
27977
+ }
27978
+ return !!permissionsMap[buildPermission(resource, operation)];
27979
+ },
27980
+ [isRbacEnabled, permissionsMap]
27981
+ );
27982
+ const value = useMemo22(
27983
+ () => ({
27984
+ policy,
27985
+ isLoading,
27986
+ isRbacEnabled,
27987
+ hasPermission,
27988
+ hasAnyPermission,
27989
+ hasAllPermissions,
27990
+ can
27991
+ }),
27992
+ [
27993
+ policy,
27994
+ isLoading,
27995
+ isRbacEnabled,
27996
+ hasPermission,
27997
+ hasAnyPermission,
27998
+ hasAllPermissions,
27999
+ can
28000
+ ]
28001
+ );
28002
+ return /* @__PURE__ */ jsx105(PermissionsContext.Provider, { value, children });
28003
+ };
28004
+
28005
+ // src/permissions/permissions-requirements-context.tsx
28006
+ import { createContext as createContext11 } from "react";
28007
+ var PermissionsRequirementsContext = createContext11(null);
28008
+
28009
+ // src/permissions/permissions-requirements-provider.tsx
28010
+ import { useCallback as useCallback27, useMemo as useMemo23, useState as useState37 } from "react";
28011
+ import { jsx as jsx106 } from "react/jsx-runtime";
28012
+ var PermissionsRequirementsProvider = ({
28013
+ children
28014
+ }) => {
28015
+ const [requirements, setRequirements] = useState37({});
28016
+ const registerRequiredPermissions = useCallback27(
28017
+ (id, requirement) => {
28018
+ setRequirements((prevState) => ({ ...prevState, [id]: requirement }));
28019
+ },
28020
+ []
28021
+ );
28022
+ const unregisterRequiredPermissions = useCallback27((id) => {
28023
+ setRequirements((prevState) => {
28024
+ const newState = { ...prevState };
28025
+ delete newState[id];
28026
+ return newState;
28027
+ });
28028
+ }, []);
28029
+ const requiredPermissions = useMemo23(() => {
28030
+ const deduped = [];
28031
+ const seen = /* @__PURE__ */ new Set();
28032
+ for (const requirement of Object.values(requirements)) {
28033
+ if (!requirement.permissions.length) {
28034
+ continue;
28035
+ }
28036
+ const key = [
28037
+ requirement.requireAll ? "all" : "any",
28038
+ [...requirement.permissions].sort().join("|"),
28039
+ requirement.source || ""
28040
+ ].join("::");
28041
+ if (seen.has(key)) {
28042
+ continue;
28043
+ }
28044
+ seen.add(key);
28045
+ deduped.push(requirement);
28046
+ }
28047
+ return deduped;
28048
+ }, [requirements]);
28049
+ const value = useMemo23(
28050
+ () => ({
28051
+ requiredPermissions,
28052
+ registerRequiredPermissions,
28053
+ unregisterRequiredPermissions
28054
+ }),
28055
+ [
28056
+ requiredPermissions,
28057
+ registerRequiredPermissions,
28058
+ unregisterRequiredPermissions
28059
+ ]
28060
+ );
28061
+ return /* @__PURE__ */ jsx106(PermissionsRequirementsContext.Provider, { value, children });
28062
+ };
28063
+
28064
+ // src/permissions/resolve-permission-props.ts
28065
+ var resolvePermissionProps = (props) => {
28066
+ if (props.permission) {
28067
+ return { permissions: [props.permission], requireAll: false };
28068
+ }
28069
+ if (props.permissions) {
28070
+ return { permissions: props.permissions, requireAll: !!props.requireAll };
28071
+ }
28072
+ if (props.resource && props.operation) {
28073
+ return {
28074
+ permissions: [buildPermission(props.resource, props.operation)],
28075
+ requireAll: false
28076
+ };
28077
+ }
28078
+ return { permissions: null, requireAll: false };
28079
+ };
28080
+
28081
+ // src/permissions/use-register-permissions.tsx
28082
+ import { useContext as useContext10, useEffect as useEffect25, useId as useId2 } from "react";
28083
+ var normalizePermissions = (permissions) => Array.from(new Set(permissions)).sort();
28084
+ var noop = () => {
28085
+ };
28086
+ var useRegisterPermissions = (permissions, options = {}) => {
28087
+ const context = useContext10(PermissionsRequirementsContext);
28088
+ const registerRequiredPermissions = context?.registerRequiredPermissions ?? noop;
28089
+ const unregisterRequiredPermissions = context?.unregisterRequiredPermissions ?? noop;
28090
+ const id = useId2();
28091
+ const enabled = options.enabled ?? true;
28092
+ const requireAll = options.requireAll ?? false;
28093
+ const source = options.source;
28094
+ const permissionsKey = permissions?.length ? normalizePermissions(permissions).join("|") : "";
28095
+ useEffect25(() => {
28096
+ if (!enabled || !permissionsKey) {
28097
+ return;
28098
+ }
28099
+ registerRequiredPermissions(id, {
28100
+ permissions: permissionsKey.split("|"),
28101
+ requireAll,
28102
+ source
28103
+ });
28104
+ return () => {
28105
+ unregisterRequiredPermissions(id);
28106
+ };
28107
+ }, [
28108
+ enabled,
28109
+ id,
28110
+ permissionsKey,
28111
+ registerRequiredPermissions,
28112
+ requireAll,
28113
+ source,
28114
+ unregisterRequiredPermissions
28115
+ ]);
28116
+ };
28117
+
28118
+ // src/permissions/permissions-requirement.tsx
28119
+ import { Fragment as Fragment10, jsx as jsx107 } from "react/jsx-runtime";
28120
+ var PermissionsRequirement = ({
28121
+ children,
28122
+ source,
28123
+ enabled = true,
28124
+ ...props
28125
+ }) => {
28126
+ const { permissions, requireAll } = resolvePermissionProps(
28127
+ props
28128
+ );
28129
+ useRegisterPermissions(permissions, { requireAll, source, enabled });
28130
+ return /* @__PURE__ */ jsx107(Fragment10, { children });
28131
+ };
28132
+
28133
+ // src/permissions/use-permissions.tsx
28134
+ import { useContext as useContext11 } from "react";
28135
+ var usePermissions = () => {
28136
+ const context = useContext11(PermissionsContext);
28137
+ if (!context) {
28138
+ throw new Error("usePermissions must be used within a PermissionsProvider");
28139
+ }
28140
+ return context;
28141
+ };
28142
+
28143
+ // src/permissions/permission-guard.tsx
28144
+ import { Fragment as Fragment11, jsx as jsx108 } from "react/jsx-runtime";
28145
+ var PermissionGuard = ({
28146
+ children,
28147
+ fallback = null,
28148
+ showLoading = false,
28149
+ loadingComponent = null,
28150
+ source,
28151
+ enabled = true,
28152
+ ...props
28153
+ }) => {
28154
+ const { permissions, requireAll } = resolvePermissionProps(
28155
+ props
28156
+ );
28157
+ useRegisterPermissions(permissions, { requireAll, source, enabled });
28158
+ const { hasAnyPermission, hasAllPermissions, isLoading } = usePermissions();
28159
+ if (isLoading && showLoading) {
28160
+ return /* @__PURE__ */ jsx108(Fragment11, { children: loadingComponent });
28161
+ }
28162
+ const hasAccess = requireAll ? hasAllPermissions(permissions ?? []) : hasAnyPermission(permissions ?? []);
28163
+ if (!hasAccess) {
28164
+ return /* @__PURE__ */ jsx108(Fragment11, { children: fallback });
28165
+ }
28166
+ return /* @__PURE__ */ jsx108(Fragment11, { children });
28167
+ };
28168
+
28169
+ // src/permissions/route-permission-guard.tsx
28170
+ import { ExclamationCircle as ExclamationCircle5 } from "@medusajs/icons";
28171
+ import { Container as Container6, Heading as Heading10, Text as Text21 } from "@medusajs/ui";
28172
+ import { useMemo as useMemo24 } from "react";
28173
+ import { useTranslation as useTranslation45 } from "react-i18next";
28174
+ import { Navigate as Navigate2, Outlet as Outlet3, useMatches } from "react-router-dom";
28175
+ import { jsx as jsx109, jsxs as jsxs70 } from "react/jsx-runtime";
28176
+ var readRequirementFromHandle = (handle) => {
28177
+ if (!handle || typeof handle !== "object") {
28178
+ return void 0;
28179
+ }
28180
+ const declared = handle;
28181
+ const rawPermissions = declared.permissions;
28182
+ if (!rawPermissions) {
28183
+ return void 0;
28184
+ }
28185
+ const permissions = Array.isArray(rawPermissions) ? rawPermissions : [rawPermissions];
28186
+ if (!permissions.every((permission) => typeof permission === "string")) {
28187
+ console.error(
28188
+ "Invalid permissions: all permissions must be strings",
28189
+ permissions
28190
+ );
28191
+ return void 0;
28192
+ }
28193
+ if (!permissions.length) {
28194
+ return void 0;
28195
+ }
28196
+ return {
28197
+ permissions,
28198
+ requireAll: declared.requireAll ?? true,
28199
+ redirectTo: declared.redirectTo
28200
+ };
28201
+ };
28202
+ var RoutePermissionGuard = () => {
28203
+ const matches = useMatches();
28204
+ const { hasAnyPermission, hasAllPermissions, isLoading } = usePermissions();
28205
+ const requirement = useMemo24(() => {
28206
+ for (let i = matches.length - 1; i >= 0; i--) {
28207
+ const found = readRequirementFromHandle(matches[i].handle);
28208
+ if (found) {
28209
+ return found;
28210
+ }
28211
+ }
28212
+ return void 0;
28213
+ }, [matches]);
28214
+ useRegisterPermissions(requirement?.permissions ?? null, {
28215
+ requireAll: requirement?.requireAll ?? true,
28216
+ source: "route"
28217
+ });
28218
+ if (isLoading || !requirement) {
28219
+ return /* @__PURE__ */ jsx109(Outlet3, {});
28220
+ }
28221
+ const hasAccess = requirement.requireAll ? hasAllPermissions(requirement.permissions) : hasAnyPermission(requirement.permissions);
28222
+ if (!hasAccess) {
28223
+ if (requirement.redirectTo) {
28224
+ return /* @__PURE__ */ jsx109(Navigate2, { to: requirement.redirectTo, replace: true });
28225
+ }
28226
+ return /* @__PURE__ */ jsx109(AccessDenied, { requirement });
28227
+ }
28228
+ return /* @__PURE__ */ jsx109(Outlet3, {});
28229
+ };
28230
+ var AccessDenied = ({
28231
+ requirement
28232
+ }) => {
28233
+ const { t: t2 } = useTranslation45();
28234
+ return /* @__PURE__ */ jsx109("div", { className: "bg-ui-bg-subtle absolute bottom-0 left-0 right-0 top-0 flex min-h-screen items-center justify-center p-4", children: /* @__PURE__ */ jsx109(Container6, { className: "max-w-md", children: /* @__PURE__ */ jsxs70("div", { className: "flex flex-col items-center gap-y-4 py-8 text-center", children: [
28235
+ /* @__PURE__ */ jsx109("div", { className: "bg-ui-bg-subtle flex h-12 w-12 items-center justify-center rounded-full", children: /* @__PURE__ */ jsx109(ExclamationCircle5, { className: "text-ui-fg-muted" }) }),
28236
+ /* @__PURE__ */ jsxs70("div", { className: "flex flex-col gap-y-1", children: [
28237
+ /* @__PURE__ */ jsx109(Heading10, { level: "h2", children: t2("permissions.accessDenied.title") }),
28238
+ /* @__PURE__ */ jsx109(Text21, { className: "text-ui-fg-subtle", children: t2("permissions.accessDenied.description") })
28239
+ ] }),
28240
+ /* @__PURE__ */ jsx109(Text21, { size: "small", className: "text-ui-fg-muted", children: t2("permissions.accessDenied.requiredPermission", {
28241
+ permission: requirement.permissions.join(", ")
28242
+ }) })
28243
+ ] }) }) });
28244
+ };
28245
+
28246
+ // src/permissions/required-permissions-section.tsx
28247
+ import { Badge as Badge9, Container as Container7, Heading as Heading11, Text as Text22 } from "@medusajs/ui";
28248
+ import { useTranslation as useTranslation46 } from "react-i18next";
28249
+
28250
+ // src/permissions/use-required-permissions.tsx
28251
+ import { useContext as useContext12 } from "react";
28252
+ var useRequiredPermissions = () => {
28253
+ const context = useContext12(PermissionsRequirementsContext);
28254
+ return context?.requiredPermissions ?? [];
28255
+ };
28256
+
28257
+ // src/permissions/required-permissions-section.tsx
28258
+ import { jsx as jsx110, jsxs as jsxs71 } from "react/jsx-runtime";
28259
+ var RequiredPermissionsSection = () => {
28260
+ const { t: t2 } = useTranslation46();
28261
+ const { isRbacEnabled } = usePermissions();
28262
+ const requirements = useRequiredPermissions();
28263
+ if (!isRbacEnabled) {
28264
+ return null;
28265
+ }
28266
+ if (!requirements.length) {
28267
+ return /* @__PURE__ */ jsxs71(Container7, { className: "flex flex-col gap-y-2 px-6 py-4", children: [
28268
+ /* @__PURE__ */ jsx110(Heading11, { level: "h2", children: t2("permissions.requiredPermissions.title") }),
28269
+ /* @__PURE__ */ jsx110(Text22, { size: "small", className: "text-ui-fg-subtle", children: t2("permissions.requiredPermissions.none") })
28270
+ ] });
28271
+ }
28272
+ return /* @__PURE__ */ jsxs71(Container7, { className: "flex flex-col gap-y-3 px-6 py-4", children: [
28273
+ /* @__PURE__ */ jsxs71("div", { className: "flex items-center justify-between", children: [
28274
+ /* @__PURE__ */ jsx110(Heading11, { level: "h2", children: t2("permissions.requiredPermissions.title") }),
28275
+ /* @__PURE__ */ jsx110(Badge9, { size: "2xsmall", rounded: "full", children: requirements.length })
28276
+ ] }),
28277
+ /* @__PURE__ */ jsx110("div", { className: "flex flex-col gap-y-3", children: requirements.map((requirement) => {
28278
+ const key = [
28279
+ requirement.requireAll ? "all" : "any",
28280
+ requirement.permissions.join("|"),
28281
+ requirement.source || ""
28282
+ ].join("::");
28283
+ return /* @__PURE__ */ jsxs71("div", { className: "flex flex-col gap-y-2", children: [
28284
+ /* @__PURE__ */ jsx110(Text22, { size: "small", className: "text-ui-fg-subtle", children: requirement.requireAll ? t2("permissions.requiredPermissions.allOf") : t2("permissions.requiredPermissions.anyOf") }),
28285
+ /* @__PURE__ */ jsx110("div", { className: "flex flex-wrap gap-1.5", children: requirement.permissions.map((permission) => /* @__PURE__ */ jsx110(Badge9, { size: "2xsmall", children: permission }, permission)) }),
28286
+ requirement.source && /* @__PURE__ */ jsx110(Text22, { size: "xsmall", className: "text-ui-fg-muted", children: t2("permissions.requiredPermissions.source", {
28287
+ source: requirement.source
28288
+ }) })
28289
+ ] }, key);
28290
+ }) })
28291
+ ] });
28292
+ };
28293
+
28294
+ // src/permissions/use-resource-permissions.tsx
28295
+ import { useMemo as useMemo25 } from "react";
28296
+ var useResourcePermissions = (resource) => {
28297
+ const { can, isLoading } = usePermissions();
28298
+ return useMemo25(
28299
+ () => ({
28300
+ canRead: can(resource, "read"),
28301
+ canCreate: can(resource, "create"),
28302
+ canUpdate: can(resource, "update"),
28303
+ canDelete: can(resource, "delete"),
28304
+ can: (operation) => can(resource, operation),
28305
+ resource,
28306
+ isLoading
28307
+ }),
28308
+ [can, resource, isLoading]
28309
+ );
28310
+ };
28311
+
27885
28312
  // src/price-lists/constants.ts
27886
28313
  var PriceListStatus = /* @__PURE__ */ ((PriceListStatus2) => {
27887
28314
  PriceListStatus2["ACTIVE"] = "active";
@@ -28102,6 +28529,12 @@ export {
28102
28529
  NoResults,
28103
28530
  OrderBy,
28104
28531
  PercentageInput,
28532
+ PermissionGuard,
28533
+ PermissionsContext,
28534
+ PermissionsProvider,
28535
+ PermissionsRequirement,
28536
+ PermissionsRequirementsContext,
28537
+ PermissionsRequirementsProvider,
28105
28538
  PlaceholderCell,
28106
28539
  PriceListDateStatus,
28107
28540
  PriceListStatus,
@@ -28112,8 +28545,10 @@ export {
28112
28545
  ProvinceSelect,
28113
28546
  Query,
28114
28547
  REFERENCE_FIELDS,
28548
+ RequiredPermissionsSection,
28115
28549
  RouteDrawer,
28116
28550
  RouteFocusModal,
28551
+ RoutePermissionGuard,
28117
28552
  SectionRow,
28118
28553
  SegmentedControl,
28119
28554
  SidebarLink,
@@ -28176,6 +28611,7 @@ export {
28176
28611
  productChangeViewHasContent,
28177
28612
  queryClient,
28178
28613
  queryKeysFactory,
28614
+ resolvePermissionProps,
28179
28615
  useCombinedRefs,
28180
28616
  useCommandHistory,
28181
28617
  useDataTable2 as useDataTable,
@@ -28186,7 +28622,11 @@ export {
28186
28622
  useExtendableTable,
28187
28623
  useExtension,
28188
28624
  useLinkQuery,
28625
+ usePermissions,
28189
28626
  useQueryParams,
28627
+ useRegisterPermissions,
28628
+ useRequiredPermissions,
28629
+ useResourcePermissions,
28190
28630
  useRouteModal,
28191
28631
  useStackedModal,
28192
28632
  useTabManagement,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercurjs/dashboard-shared",
3
- "version": "2.3.2-canary.1",
3
+ "version": "2.3.2-canary.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mercurjs/mercur",
@@ -23,7 +23,7 @@
23
23
  "build": "tsup"
24
24
  },
25
25
  "dependencies": {
26
- "@mercurjs/client": "2.3.2-canary.1",
26
+ "@mercurjs/client": "2.3.2-canary.2",
27
27
  "@ariakit/react": "^0.4.15",
28
28
  "@babel/runtime": "^8.0.0",
29
29
  "@dnd-kit/core": "^6.1.0",
@@ -64,9 +64,9 @@
64
64
  },
65
65
  "devDependencies": {
66
66
  "@medusajs/types": "2.18.0",
67
- "@mercurjs/core": "2.3.2-canary.1",
68
- "@mercurjs/dashboard-sdk": "2.3.2-canary.1",
69
- "@mercurjs/types": "2.3.2-canary.1",
67
+ "@mercurjs/core": "2.3.2-canary.2",
68
+ "@mercurjs/dashboard-sdk": "2.3.2-canary.2",
69
+ "@mercurjs/types": "2.3.2-canary.2",
70
70
  "@types/lodash.debounce": "^4.0.8",
71
71
  "@types/react": "^18.3.2",
72
72
  "@types/react-dom": "^18.2.22",