@feedmepos/mf-hrm-portal 2.1.1-dev.5 → 2.1.1-dev.7

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 (60) hide show
  1. package/dist/ApprovalCodeList-CQApO-t4.js +728 -0
  2. package/dist/{AuditLogList-CXUkbDyT.js → AuditLogList-DH2ZvAVE.js} +244 -240
  3. package/dist/ConditionFormHost.vue_vue_type_script_setup_true_lang-B5OgqCR0.js +387 -0
  4. package/dist/EmployeeList-B1Q4RH2r.js +883 -0
  5. package/dist/{GrantUserList-DKP4MBQM.js → GrantUserList-DURXYzRs.js} +140 -137
  6. package/dist/{Main-CyCfz4ka.js → Main-B06aoNy1.js} +1033 -1029
  7. package/dist/{Main-Cz1pfe2k.js → Main-BC7IPhbz.js} +7 -7
  8. package/dist/Main-BRrcfqXc.js +121 -0
  9. package/dist/PermissionSetList-CiVFvnlW.js +393 -0
  10. package/dist/PortalPermissionEditor.vue_vue_type_script_setup_true_lang-BAmLIGEn.js +4122 -0
  11. package/dist/{PortalUserSearcher.vue_vue_type_style_index_0_scoped_a439fdd8_lang-BJp9hEfv.js → PortalUserSearcher.vue_vue_type_style_index_0_scoped_827dfd60_lang-DVVSBIx2.js} +1 -0
  12. package/dist/ResponsiveFabButton.vue_vue_type_script_setup_true_lang-QrqYEy8i.js +42 -0
  13. package/dist/RoleList-BVFWURdS.js +685 -0
  14. package/dist/TableEmptyState.vue_vue_type_script_setup_true_lang-IfAZmWr0.js +37 -0
  15. package/dist/TeamMemberList-DlurP2WR.js +1215 -0
  16. package/dist/{TimesheetList-s_bP5OGA.js → TimesheetList-C4G2Va4m.js} +118 -112
  17. package/dist/{TimesheetList.vue_vue_type_style_index_0_scoped_91afda4e_lang-BCLv-2Re.js → TimesheetList.vue_vue_type_style_index_0_scoped_2c7a177a_lang-BJujYg6e.js} +2 -2
  18. package/dist/{app-BP0oihBR.js → app-CGL_lSgW.js} +5073 -2360
  19. package/dist/app.js +1 -1
  20. package/dist/{dayjs.min-BTSZLci9.js → dayjs.min-BXDtus8O.js} +1 -1
  21. package/dist/{employee-BPv1j6T4.js → employee-CVZUQI_y.js} +3 -3
  22. package/dist/{iteration-BNA1cEos.js → iteration-BXcl549C.js} +1 -1
  23. package/dist/{lodash-DKG9IyB-.js → lodash-CmrNrQXn.js} +1 -1
  24. package/dist/{role-BS3LFO8u.js → role-Ct8MPQeS.js} +1 -1
  25. package/dist/rule-M8SC_Jhl.js +1058 -0
  26. package/dist/src/components/CheckboxInput.vue.d.ts +1 -1
  27. package/dist/src/components/button/ResponsiveFabButton.vue.d.ts +7 -1
  28. package/dist/src/components/table/TableEmptyState.vue.d.ts +7 -15
  29. package/dist/src/composables/useBasePermission.d.ts +2 -1
  30. package/dist/src/composables/useConditionDraft.d.ts +40 -0
  31. package/dist/src/composables/useCoverConditionOverride.d.ts +61 -0
  32. package/dist/src/helpers/business-timezone.d.ts +22 -0
  33. package/dist/src/helpers/filter.d.ts +15 -0
  34. package/dist/src/helpers/permission-reconcile.d.ts +15 -0
  35. package/dist/src/helpers/permission-reconcile.test.d.ts +1 -0
  36. package/dist/src/helpers/permission-set.d.ts +8 -4
  37. package/dist/src/helpers/rule.d.ts +2 -1
  38. package/dist/src/stores/permission-set.d.ts +12 -3
  39. package/dist/src/types/approval-code.d.ts +29 -48
  40. package/dist/src/views/hr/employee/EmployeeList.vue.d.ts +1 -0
  41. package/dist/src/views/team/components/BasePermission.vue.d.ts +13 -0
  42. package/dist/src/views/team/condition-builders/ConditionFormHost.vue.d.ts +22 -0
  43. package/dist/src/views/team/condition-builders/ConditionFormRenderer.vue.d.ts +23 -0
  44. package/dist/src/views/team/condition-builders/component-map.d.ts +4 -0
  45. package/dist/src/views/team/condition-builders/condition-form-dedupe.d.ts +23 -0
  46. package/dist/src/views/team/locales/index.d.ts +80 -0
  47. package/dist/style.css +1 -1
  48. package/dist/tsconfig.app.tsbuildinfo +1 -1
  49. package/dist/{useReportPermissions-J3-DjopO.js → useReportPermissions-COONOIe1.js} +3 -3
  50. package/package.json +2 -2
  51. package/dist/ApprovalCodeList-CXJyMKEg.js +0 -757
  52. package/dist/CollapsibleWrapper.vue_vue_type_script_setup_true_lang-BLJlmPVa.js +0 -58
  53. package/dist/EmployeeList-BSfUFTcN.js +0 -899
  54. package/dist/Main-CblWfQk_.js +0 -122
  55. package/dist/PermissionSetList-BwyblSeE.js +0 -357
  56. package/dist/PortalPermissionEditor.vue_vue_type_script_setup_true_lang-BJ-PZ2Nv.js +0 -3458
  57. package/dist/ResponsiveFabButton.vue_vue_type_script_setup_true_lang-Cn-3MRBO.js +0 -38
  58. package/dist/RoleList-CXYgCmHp.js +0 -567
  59. package/dist/TeamMemberList-CnLbqaPS.js +0 -1131
  60. package/dist/rule-DaSLVZWm.js +0 -996
@@ -141,8 +141,8 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
141
141
  }>> & Readonly<{
142
142
  "onUpdate:model-value"?: ((...args: any[]) => any) | undefined;
143
143
  }>, {
144
- max: number;
145
144
  min: number;
145
+ max: number;
146
146
  disable: boolean;
147
147
  showAllToggle: string | boolean;
148
148
  includeDisabledOptionsWhenSelectAll: boolean;
@@ -1,3 +1,9 @@
1
1
  import { type FmButtonProps } from '@feedmepos/ui-library';
2
- declare const _default: import("vue").DefineComponent<FmButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FmButtonProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
2
+ interface Props extends FmButtonProps {
3
+ /** Whether the page has a fixed bottom footer (e.g. a pagination bar) the mobile FAB should raise above. */
4
+ hasFooterBelow?: boolean;
5
+ }
6
+ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
7
+ hasFooterBelow: boolean;
8
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
3
9
  export default _default;
@@ -5,30 +5,22 @@ interface Props {
5
5
  isEmpty: boolean;
6
6
  isLoading?: boolean;
7
7
  /**
8
- * At what width do the column titles disappear? The table's column labels are gone at/below
9
- * this breakpoint, so the empty state should move outside instead of overlaying the table
10
- * body. Must mirror the wrapped FmTable's own `shrink-at` prop (both default to "sm"); pass
11
- * `false` if the table never collapses.
8
+ * At what width do the column titles disappear? Below/at this breakpoint the message is
9
+ * styled as a standalone bordered card instead of blending flat under the table header. Must
10
+ * mirror the wrapped FmTable's own `shrink-at` prop (both default to "sm"); pass `false` if
11
+ * the table never collapses.
12
12
  */
13
13
  collapseAt?: Breakpoints | false;
14
- /**
15
- * At what width do we hide the table completely because there's a separate mobile view taking
16
- * over? For callers that render their own alternate view (e.g. a mobile FmList) at/below this
17
- * breakpoint. Pass `false` if the table has no alternate view and should stay visible at every
18
- * breakpoint.
19
- */
20
- hideTableBelow?: Breakpoints | false;
21
14
  }
22
- declare var __VLS_5: {
23
- isTableCollapsed: boolean;
15
+ declare var __VLS_1: {
16
+ isEmpty: boolean;
24
17
  };
25
18
  type __VLS_Slots = {} & {
26
- default?: (props: typeof __VLS_5) => any;
19
+ default?: (props: typeof __VLS_1) => any;
27
20
  };
28
21
  declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
29
22
  isLoading: boolean;
30
23
  collapseAt: Breakpoints | false;
31
- hideTableBelow: Breakpoints | false;
32
24
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
25
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
34
26
  export default _default;
@@ -7,6 +7,7 @@ export type CategoryGroup = {
7
7
  options: {
8
8
  label: string;
9
9
  value: string;
10
+ optionKey: string;
10
11
  }[];
11
12
  selectedSubjects: string[];
12
13
  isMultiAction: boolean;
@@ -25,7 +26,7 @@ export declare function useBasePermission(modelValue: Readonly<Ref<FdoPermission
25
26
  toggleAllManage: (group: CategoryGroup) => void;
26
27
  isManageChecked: (group: CategoryGroup, subject: string) => boolean;
27
28
  isManageIndeterminate: (group: CategoryGroup, subject: string) => boolean;
28
- toggleManage: (_group: CategoryGroup, subject: string, checked: boolean) => void;
29
+ toggleManage: (group: CategoryGroup, subject: string, checked: boolean) => void;
29
30
  hasNonManageActions: (group: CategoryGroup, subject: string) => boolean;
30
31
  isActionChecked: (group: CategoryGroup, subject: string, action: string) => boolean;
31
32
  isRowFullyCoveredByCover: (group: CategoryGroup, subject: string) => boolean;
@@ -0,0 +1,40 @@
1
+ import { type Ref } from 'vue';
2
+ /**
3
+ * Local draft state for condition editing — tracks form field edits and
4
+ * inherit/custom mode changes for each builder, keyed by `${subject}::${builderKey}`.
5
+ *
6
+ * Drafts remain local until explicitly committed via `applyDrafts`. Closing
7
+ * a popover/menu without applying discards uncommitted changes.
8
+ */
9
+ export interface DraftKey {
10
+ subject: string;
11
+ builderKey: string;
12
+ }
13
+ export interface ConditionDraft {
14
+ /** The local draft form value for this builder. */
15
+ formValue: unknown;
16
+ /** Local draft inherit/custom mode for builders under a cover subject. */
17
+ inheritMode?: 'inherit' | 'custom';
18
+ }
19
+ /**
20
+ * Creates a composable to manage local draft state for condition editing.
21
+ *
22
+ * @param committedSource - Ref to the committed state (e.g., modelValue, reportExtraConditions)
23
+ * used to detect external changes and reset drafts accordingly
24
+ */
25
+ export declare function useConditionDraft<T>(committedSource?: Ref<T>): {
26
+ drafts: Ref<Map<string, {
27
+ formValue: unknown;
28
+ inheritMode?: "inherit" | "custom" | undefined;
29
+ }> & Omit<Map<string, ConditionDraft>, keyof Map<any, any>>, Map<string, ConditionDraft> | (Map<string, {
30
+ formValue: unknown;
31
+ inheritMode?: "inherit" | "custom" | undefined;
32
+ }> & Omit<Map<string, ConditionDraft>, keyof Map<any, any>>)>;
33
+ setDraft: (key: DraftKey, draft: ConditionDraft) => void;
34
+ getDraft: (key: DraftKey) => ConditionDraft | undefined;
35
+ hasDraftsForSubject: (subject: string) => boolean;
36
+ getDraftsForSubject: (subject: string) => Map<string, ConditionDraft>;
37
+ clearDraftsForSubject: (subject: string) => void;
38
+ clearAllDrafts: () => void;
39
+ clearDraft: (key: DraftKey) => void;
40
+ };
@@ -0,0 +1,61 @@
1
+ import { type Ref, type ComputedRef } from 'vue';
2
+ import type { FdoPermissionRule } from '@feedmepos/core/entity';
3
+ import { type IFdtoPortalPermissionsWithVisibilityCondition } from '@feedmepos/hrm-permission';
4
+ import { type ConditionBuilder, type ConditionSummary } from '@feedmepos/hrm-permission/condition-builder';
5
+ /**
6
+ * Generic "category cover" machinery for the portal permission editor: any
7
+ * subject whose permission-definition category declares a cover (see
8
+ * `getCoverSubjectFor`) can have its condition-builder settings either
9
+ * inherit the cover subject's condition or hold a custom override — and the
10
+ * cover subject itself can push its condition down onto every individual
11
+ * override in the category.
12
+ *
13
+ * Zero knowledge of any specific subject, category, or builder lives here —
14
+ * everything is driven by the `defs` list (permission definitions carrying
15
+ * `category`/`isCategoryCover`) and the `ConditionBuilder` contract
16
+ * (`ownedFields`, `appliesToActions`, `summarizeForm`).
17
+ */
18
+ /** The minimum shape the editor's per-row entry needs to expose for cover/inherit resolution. */
19
+ export interface CoverConditionEntry {
20
+ /** Index of this subject's raw rule in the model, or null when no direct rule exists yet. */
21
+ index: number | null;
22
+ subject: string;
23
+ label: string;
24
+ actions: readonly string[];
25
+ }
26
+ /** Resolution handles passed to `onOverridesConflict` — the caller (UI) decides which to invoke. */
27
+ export interface CoverOverrideResolution {
28
+ /** Strip this builder's fragment from every covered override rule (dropping a rule once it carries no more conditions and its actions are covered). */
29
+ override: () => void;
30
+ /** Leave every covered override rule untouched. */
31
+ keep: () => void;
32
+ }
33
+ export interface UpdateBuilderConditionOptions {
34
+ /** Create a direct rule even if the resulting condition is empty (used when explicitly switching to "custom"). */
35
+ forceCreateRule?: boolean;
36
+ afterCommit?: () => void;
37
+ /**
38
+ * Invoked instead of auto-resolving when this write touches a cover
39
+ * subject's row and other subjects in the same category hold a
40
+ * conditioned override on this builder. Omit only for callers that don't
41
+ * need a confirmation step — the write then defaults to keeping the
42
+ * overrides untouched (the non-destructive choice).
43
+ */
44
+ onOverridesConflict?: (resolution: CoverOverrideResolution) => void;
45
+ }
46
+ export interface BuilderConditionDraft {
47
+ builder: ConditionBuilder;
48
+ form: unknown;
49
+ mode: 'inherit' | 'custom';
50
+ }
51
+ export declare function useCoverConditionOverride(rules: Ref<FdoPermissionRule[]>, defs: ComputedRef<IFdtoPortalPermissionsWithVisibilityCondition[]>, updateRules: (next: FdoPermissionRule[]) => void): {
52
+ getCoverSubject: (subject: string) => string | undefined;
53
+ coverLabel: (subject: string) => string | undefined;
54
+ shouldShowInheritToggle: (entry: CoverConditionEntry, builder: ConditionBuilder) => boolean;
55
+ inheritMode: (entry: CoverConditionEntry, builder: ConditionBuilder) => "inherit" | "custom";
56
+ setInheritMode: (entry: CoverConditionEntry, builder: ConditionBuilder, mode: "inherit" | "custom") => void;
57
+ inheritedSummary: (entry: CoverConditionEntry, builder: ConditionBuilder, timeZone: string) => ConditionSummary | undefined;
58
+ getEffectiveActions: (subject: string) => readonly string[];
59
+ updateBuilderCondition: (entry: CoverConditionEntry, builder: ConditionBuilder, form: unknown, options?: UpdateBuilderConditionOptions) => void;
60
+ applyConditionDrafts: (entry: CoverConditionEntry, updates: BuilderConditionDraft[], options?: UpdateBuilderConditionOptions) => void;
61
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Business timezone resolution — a pure mapping from country code to IANA
3
+ * timezone, used as a fallback when a business has no explicit timezone
4
+ * configured. Kept independent of any store so it's testable in isolation;
5
+ * callers resolve the business/country from their own store and pass the
6
+ * plain values in.
7
+ */
8
+ /** IANA timezone for a country code; falls back to the runtime's local zone for unknown/missing codes. */
9
+ export declare function getCountryTimezone(country?: string): string;
10
+ /** Minimal shape a business needs for timezone resolution. */
11
+ export interface BusinessTimezoneSource {
12
+ timezone?: string;
13
+ timeZone?: string;
14
+ country?: string;
15
+ }
16
+ /**
17
+ * Resolve the effective IANA timezone for a business: an explicit business
18
+ * timezone field wins (`timezone`, then legacy `timeZone`), otherwise falls
19
+ * back to the country's default timezone (business's own country first, then
20
+ * `fallbackCountry`).
21
+ */
22
+ export declare function resolveBusinessTimezone(business: BusinessTimezoneSource | undefined, fallbackCountry?: string): string;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Sentinel value used in multi-select list filters (assigned role, restaurant, etc.) to
3
+ * represent "this field is empty/unassigned for the record". Must match the backend's
4
+ * `UNASSIGNED_FILTER_VALUE` in src/common/helpers/filter.ts.
5
+ *
6
+ * When sending a filter that includes this value to the backend, it must be the first element
7
+ * of the array -- the backend only checks index 0 to detect it (cheap even for filters with
8
+ * large option sets), it does not scan the whole list.
9
+ */
10
+ export declare const UNASSIGNED_FILTER_VALUE = "__unassigned__";
11
+ /**
12
+ * Reorders a multi-select filter's selected values so the "unassigned" sentinel (if present)
13
+ * is always first, matching what the backend's `splitUnassignedFilter` expects.
14
+ */
15
+ export declare function withUnassignedFirst(values?: string[]): string[] | undefined;
@@ -0,0 +1,15 @@
1
+ import type { FdoPermissionRule } from '@feedmepos/core/entity';
2
+ import type { IFdtoPortalPermissionsWithVisibilityCondition } from '@feedmepos/hrm-permission';
3
+ /**
4
+ * Reconcile a BasePermission update (which works with the expanded view) back to the raw modelValue.
5
+ *
6
+ * - Subject removed that was directly in raw → remove it
7
+ * - Subject removed that was system-expanded (not in raw) → add inverted rule to block re-expansion
8
+ * - Subject removed that was stored directly AND still reachable via expansion from remaining
9
+ * rules (e.g. previously materialized by an earlier save) → remove direct rule AND add inverted,
10
+ * otherwise materializeSystemPermissionSetOverrides re-injects it during save
11
+ * - Subject added that had an inverted rule → remove the inverted rule (expansion re-enables it)
12
+ * - Subject added that is genuinely new → add it to raw
13
+ * - Subject unchanged but actions changed → update actions in raw (multi-action permissions)
14
+ */
15
+ export declare function reconcileBasePermissionUpdate(prevExpandedRules: FdoPermissionRule[], newExpandedRules: FdoPermissionRule[], rawRules: FdoPermissionRule[], findPermissionDef: (subject: string) => IFdtoPortalPermissionsWithVisibilityCondition | undefined): FdoPermissionRule[];
@@ -0,0 +1 @@
1
+ export {};
@@ -71,10 +71,14 @@ export declare function filterStalePermissions(permissions: FdoPermissionRule[],
71
71
  * `allCustomReports` positive grant, preventing redundant data from being saved.
72
72
  *
73
73
  * - If the cover rule has `manage` selected → every individual rule in that bucket is
74
- * fully covered and dropped entirely.
75
- * - Otherwise, only covered non-manage actions are stripped from individual rules.
76
- * If `manage` was present and any non-manage action is stripped (breaking the
77
- * "manage = all non-manage selected" invariant), `manage` is also stripped.
74
+ * fully covered and dropped entirely only when the cover condition also covers the
75
+ * individual rule.
76
+ * - Otherwise, only covered non-manage actions are stripped from individual rules when
77
+ * the cover condition also covers the individual rule.
78
+ * `manage` is a real CASL wildcard grant that implies every action, not merely a
79
+ * shorthand for "all non-manage actions selected" — a rule carrying `manage` is
80
+ * pushed through UNCHANGED (a cover lacking `manage` cannot cover it). Stripping its
81
+ * constituent actions and then dropping `manage` would destroy the user's grant.
78
82
  * Rules that end up with no remaining actions are dropped.
79
83
  *
80
84
  * Inverted rules and the cover subjects themselves pass through untouched.
@@ -20,7 +20,8 @@ export declare function getMaxDiscount(rules: FdoPermissionRule[]): number;
20
20
  export declare function getManagableRestaurantIds(rules: (FdoPermissionRule | RawRule)[]): string[] | null;
21
21
  export declare function hasAdminRule(rules: FdoPermissionRule[]): boolean;
22
22
  export declare function usableRoleIds(rules: (FdoPermissionRule | RawRule)[]): string[] | null;
23
- export declare function fromValue(values: string[], maxDiscountPercentage: number): {
23
+ export declare function extractReportExtraCondition(rule: FdoPermissionRule): Record<string, unknown> | undefined;
24
+ export declare function fromValue(values: string[], maxDiscountPercentage: number, periodConditions?: Map<string, Record<string, unknown>>): {
24
25
  subject: string;
25
26
  actions: ("delete" | "create" | "update" | "read" | "manage")[];
26
27
  conditions: object;
@@ -1,5 +1,6 @@
1
1
  import type { IFdtoPermissionSet, IFdtoCreatePermissionSetReq, IFdtoUpdatePermissionSetReq } from '@/types/permission-set';
2
2
  export declare const usePermissionSetStore: () => {
3
+ isLoading: import("vue").Ref<boolean, boolean>;
3
4
  permissionSets: import("vue").Ref<{
4
5
  id: string;
5
6
  permissions: {
@@ -30,6 +31,7 @@ export declare const usePermissionSetStore: () => {
30
31
  name: string;
31
32
  }[]>;
32
33
  $state: {
34
+ isLoading: boolean;
33
35
  permissionSets: {
34
36
  id: string;
35
37
  permissions: {
@@ -75,12 +77,14 @@ export declare const usePermissionSetStore: () => {
75
77
  businessId: string;
76
78
  name: string;
77
79
  }[]>;
80
+ isLoading: import("vue").Ref<boolean, boolean>;
78
81
  readPermissionSets: () => Promise<void>;
79
82
  createPermissionSet: (dto: IFdtoCreatePermissionSetReq) => Promise<void>;
80
83
  updatePermissionSet: (id: string, dto: IFdtoUpdatePermissionSetReq) => Promise<void>;
81
84
  deletePermissionSet: (id: string) => Promise<void>;
82
- }, "permissionSets">>;
85
+ }, "isLoading" | "permissionSets">>;
83
86
  $patch(partialState: import("pinia")._DeepPartial<{
87
+ isLoading: boolean;
84
88
  permissionSets: {
85
89
  id: string;
86
90
  permissions: {
@@ -98,6 +102,7 @@ export declare const usePermissionSetStore: () => {
98
102
  }[];
99
103
  }>): void;
100
104
  $patch<F extends (state: {
105
+ isLoading: boolean;
101
106
  permissionSets: {
102
107
  id: string;
103
108
  permissions: {
@@ -145,11 +150,12 @@ export declare const usePermissionSetStore: () => {
145
150
  businessId: string;
146
151
  name: string;
147
152
  }[]>;
153
+ isLoading: import("vue").Ref<boolean, boolean>;
148
154
  readPermissionSets: () => Promise<void>;
149
155
  createPermissionSet: (dto: IFdtoCreatePermissionSetReq) => Promise<void>;
150
156
  updatePermissionSet: (id: string, dto: IFdtoUpdatePermissionSetReq) => Promise<void>;
151
157
  deletePermissionSet: (id: string) => Promise<void>;
152
- }, "permissionSets">>, options?: {
158
+ }, "isLoading" | "permissionSets">>, options?: {
153
159
  detached?: boolean;
154
160
  } & import("vue").WatchOptions): () => void;
155
161
  $onAction(callback: import("pinia").StoreOnActionListener<"permissionSet", Pick<{
@@ -182,11 +188,12 @@ export declare const usePermissionSetStore: () => {
182
188
  businessId: string;
183
189
  name: string;
184
190
  }[]>;
191
+ isLoading: import("vue").Ref<boolean, boolean>;
185
192
  readPermissionSets: () => Promise<void>;
186
193
  createPermissionSet: (dto: IFdtoCreatePermissionSetReq) => Promise<void>;
187
194
  updatePermissionSet: (id: string, dto: IFdtoUpdatePermissionSetReq) => Promise<void>;
188
195
  deletePermissionSet: (id: string) => Promise<void>;
189
- }, "permissionSets">, Pick<{
196
+ }, "isLoading" | "permissionSets">, Pick<{
190
197
  permissionSets: import("vue").Ref<{
191
198
  id: string;
192
199
  permissions: {
@@ -216,6 +223,7 @@ export declare const usePermissionSetStore: () => {
216
223
  businessId: string;
217
224
  name: string;
218
225
  }[]>;
226
+ isLoading: import("vue").Ref<boolean, boolean>;
219
227
  readPermissionSets: () => Promise<void>;
220
228
  createPermissionSet: (dto: IFdtoCreatePermissionSetReq) => Promise<void>;
221
229
  updatePermissionSet: (id: string, dto: IFdtoUpdatePermissionSetReq) => Promise<void>;
@@ -250,6 +258,7 @@ export declare const usePermissionSetStore: () => {
250
258
  businessId: string;
251
259
  name: string;
252
260
  }[]>;
261
+ isLoading: import("vue").Ref<boolean, boolean>;
253
262
  readPermissionSets: () => Promise<void>;
254
263
  createPermissionSet: (dto: IFdtoCreatePermissionSetReq) => Promise<void>;
255
264
  updatePermissionSet: (id: string, dto: IFdtoUpdatePermissionSetReq) => Promise<void>;
@@ -5,7 +5,8 @@ export type ApprovalCodePermission = (typeof APPROVAL_CODE_PERMISSIONS)[number];
5
5
  * stored/returned by the backend. Mirrors `FdoApprovalCodeRule` from
6
6
  * `apps/hrm-backend/src/grant-user/grant-user.entity.ts`. `conditions` is
7
7
  * `null` (not just absent) for keys whose full-POS counterpart carries no
8
- * conditions at all (see `APPROVAL_CODE_RULE_TEMPLATES`).
8
+ * conditions at all (see `APPROVAL_CODE_RULE_TEMPLATES` at
9
+ * `apps/hrm-backend/src/grant-user/approval-code-rules.ts`).
9
10
  */
10
11
  export interface IFdtoApprovalCodeRule {
11
12
  key: ApprovalCodePermission;
@@ -13,41 +14,6 @@ export interface IFdtoApprovalCodeRule {
13
14
  actions: string[];
14
15
  conditions?: Record<string, unknown> | null;
15
16
  }
16
- /**
17
- * The rule template (subject/actions/conditions, minus `key`) for every
18
- * approval-code permission the portal can grant. The portal is now the
19
- * owner of these shapes (KDI-4982 Task A2) — the backend only validates the
20
- * shape of whatever rule it receives, it no longer composes rules itself.
21
- * These shapes are not arbitrary: they mirror `FullPosPermissions`
22
- * (`packages/permission/src/common/full-pos-permissions.ts`) exactly, entry
23
- * for entry, so a code granting one of these keys evaluates identically to
24
- * the corresponding rule a full-POS-permission staff member already has.
25
- * That's why `refund`/`openDrawer`/`counter` carry `conditions: null` — full
26
- * POS has no `operation` condition on those subjects — and why `discount`
27
- * always carries an `adjustment` condition (full POS's unbounded discount
28
- * rule is `adjustment: { $lte: 1 }`, i.e. "up to 100%", not "no adjustment
29
- * condition at all"). `counter` is a single key (not separate open/close
30
- * keys) because POS guards both opening and closing a counter with the same
31
- * `counterRecord`/`manage` permission — two identical rules would just be
32
- * redundant.
33
- */
34
- export declare const APPROVAL_CODE_RULE_TEMPLATES: Record<ApprovalCodePermission, {
35
- subject: string;
36
- actions: string[];
37
- conditions: Record<string, unknown> | null;
38
- }>;
39
- /**
40
- * Compose the full CASL-shaped rule for `key`, ready to send to the backend
41
- * as-is, saved shape-identical to the corresponding `FullPosPermissions`
42
- * entry. Keys whose template has `conditions: null` always return `null` —
43
- * there's nothing to clone. Only `discount` honors `params.maxPercentage`,
44
- * setting the `adjustment.$lte` fraction (defaulting to `1`, i.e. no
45
- * effective ceiling, matching full POS's unbounded discount rule) — every
46
- * other object-conditions key ignores `params` if passed.
47
- */
48
- export declare function buildApprovalCodeRule(key: ApprovalCodePermission, params?: {
49
- maxPercentage?: number;
50
- }): IFdtoApprovalCodeRule;
51
17
  export interface IFdtoApprovalCode {
52
18
  _id: string;
53
19
  businessId: string;
@@ -64,20 +30,15 @@ export interface IFdtoApprovalCode {
64
30
  createdAt: string;
65
31
  updatedAt: string;
66
32
  }
67
- export interface IFdtoCreateApprovalCodeReq {
68
- name: string;
69
- restaurantId: string;
70
- permissions: IFdtoApprovalCodeRule[];
71
- count: number;
72
- }
73
33
  /**
74
34
  * Read the discount ceiling back out of a rule's `conditions.adjustment.$lte`
75
35
  * fraction (0-1) as a whole percentage (1-100). Returns `undefined` when the
76
36
  * rule has no adjustment ceiling, when `conditions` is `null` (full-POS-style
77
37
  * null-condition rules never carry an adjustment), or when the ceiling is
78
38
  * 100% — `$lte: 1` is the unbounded/default form (see
79
- * `APPROVAL_CODE_RULE_TEMPLATES.discount`), so showing "≤100%" would be
80
- * meaningless noise on every plain discount grant.
39
+ * `APPROVAL_CODE_RULE_TEMPLATES.discount` at
40
+ * `apps/hrm-backend/src/grant-user/approval-code-rules.ts`), so showing
41
+ * "≤100%" would be meaningless noise on every plain discount grant.
81
42
  */
82
43
  export declare function getRuleAdjustmentCeilingPercentage(rule: IFdtoApprovalCodeRule): number | undefined;
83
44
  /**
@@ -110,15 +71,29 @@ export declare function validateMaxPercentage(value: number | string | undefined
110
71
  * Assumes the value has already passed `validateMaxPercentage`.
111
72
  */
112
73
  export declare function resolveMaxPercentage(value: number | string | undefined): number | undefined;
74
+ /** One requested permission on a new access code, matching the backend's
75
+ * `FdtoApprovalCodePermissionReq`: a rule key plus, for `discount` only, a
76
+ * whole-percentage ceiling (1-100). The backend composes the stored CASL rule
77
+ * from the key — the portal no longer sends rule shapes. */
78
+ export interface IFdtoApprovalCodePermissionReq {
79
+ key: ApprovalCodePermission;
80
+ maxPercentage?: number;
81
+ }
82
+ export interface IFdtoCreateApprovalCodeReq {
83
+ name: string;
84
+ restaurantId: string;
85
+ permissions: IFdtoApprovalCodePermissionReq[];
86
+ count: number;
87
+ }
113
88
  /**
114
89
  * A selectable action option in the create form / list filter (KDI-4982 UI
115
90
  * redesign). The 8 options mirror the POS v7 passcode prompts exactly:
116
91
  * `claimDeposit` and `payment` are not offered (neither has a passcode prompt
117
- * on POS v7) even though both have `APPROVAL_CODE_RULE_TEMPLATES` entries
118
- * matching full POS. `changePrice` is offered as "Input Custom Price" — POS
92
+ * on POS v7) even though both are valid `ApprovalCodePermission` keys the
93
+ * backend accepts. `changePrice` is offered as "Input Custom Price" — POS
119
94
  * prompts for a passcode when a custom price is keyed in. `counter` covers
120
- * both opening and closing a counter (one `counterRecord`/`manage` rule, see
121
- * `APPROVAL_CODE_RULE_TEMPLATES.counter`). `printReceipt` was dropped
95
+ * both opening and closing a counter (one `counterRecord`/`manage` rule on
96
+ * the backend). `printReceipt` was dropped
122
97
  * entirely (no full-POS counterpart, and this feature has no pre-existing
123
98
  * stored data to keep rendering) — there is a 1:1 mapping between every
124
99
  * option and its single `ApprovalCodePermission` rule key, so each option
@@ -131,6 +106,12 @@ export interface AccessCodeActionOption {
131
106
  }
132
107
  export type AccessCodeActionOptionKey = AccessCodeActionOption['key'];
133
108
  export declare const ACCESS_CODE_ACTION_OPTIONS: AccessCodeActionOption[];
109
+ /**
110
+ * Map the form's selected action options to permission-key requests.
111
+ * `maxPercentage` is the raw form input (see `resolveMaxPercentage`) and is
112
+ * attached to the `discount` key only — the backend rejects it elsewhere.
113
+ */
114
+ export declare function buildPermissionRequests(selected: AccessCodeActionOptionKey[], maxPercentage: number | string | undefined): IFdtoApprovalCodePermissionReq[];
134
115
  /** When a code stopped being usable: used wins over revoked (a code can only
135
116
  * be one or the other, but usedAt is the historically-populated field). */
136
117
  export declare function getResolvedAt(code: Pick<IFdtoApprovalCode, 'usedAt' | 'revokedAt'>): string | undefined;
@@ -41,5 +41,6 @@ declare const _default: import("vue").DefineComponent<{}, {
41
41
  since: string;
42
42
  })[]>;
43
43
  exportEmployees: typeof exportEmployees;
44
+ getHasPaginationFooter: () => boolean;
44
45
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
45
46
  export default _default;
@@ -5,10 +5,23 @@ declare var __VLS_21: `checkbox-${string}-append`, __VLS_22: {
5
5
  option: {
6
6
  label: string;
7
7
  value: string;
8
+ optionKey: string;
8
9
  };
10
+ isEffectiveSelected: boolean;
11
+ isCoveredByCover: boolean;
12
+ }, __VLS_25: `checkbox-${string}-icon`, __VLS_26: {
13
+ option: {
14
+ label: string;
15
+ value: string;
16
+ optionKey: string;
17
+ };
18
+ isEffectiveSelected: boolean;
19
+ isCoveredByCover: boolean;
9
20
  };
10
21
  type __VLS_Slots = {} & {
11
22
  [K in NonNullable<typeof __VLS_21>]?: (props: typeof __VLS_22) => any;
23
+ } & {
24
+ [K in NonNullable<typeof __VLS_25>]?: (props: typeof __VLS_26) => any;
12
25
  };
13
26
  declare const __VLS_component: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
14
27
  modelValue: {
@@ -0,0 +1,22 @@
1
+ import type { ConditionBuilder } from '@feedmepos/hrm-permission/condition-builder';
2
+ type __VLS_Props = {
3
+ builder: ConditionBuilder;
4
+ modelValue: unknown;
5
+ disable?: boolean;
6
+ optionProviders?: Record<string, Array<{
7
+ value: string;
8
+ label: string;
9
+ }>>;
10
+ };
11
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
12
+ "update:modelValue": (value: unknown) => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
14
+ "onUpdate:modelValue"?: ((value: unknown) => any) | undefined;
15
+ }>, {
16
+ optionProviders: Record<string, Array<{
17
+ value: string;
18
+ label: string;
19
+ }>>;
20
+ disable: boolean;
21
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import type { ConditionFormConfig, ConditionFormState } from '@feedmepos/hrm-permission/condition-builder';
2
+ type __VLS_Props = {
3
+ config: ConditionFormConfig;
4
+ modelValue: ConditionFormState;
5
+ disabled?: boolean;
6
+ /** name → option list, for fields whose options use `{ provider }`. */
7
+ optionProviders?: Record<string, Array<{
8
+ value: string;
9
+ label: string;
10
+ }>>;
11
+ };
12
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
13
+ "update:modelValue": (value: ConditionFormState) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ "onUpdate:modelValue"?: ((value: ConditionFormState) => any) | undefined;
16
+ }>, {
17
+ disabled: boolean;
18
+ optionProviders: Record<string, Array<{
19
+ value: string;
20
+ label: string;
21
+ }>>;
22
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import { type Component } from 'vue';
2
+ /** Resolve a form component key to a cached async component, or undefined when no
3
+ * component is registered for that key (the host then renders nothing). */
4
+ export declare function resolveBuilderComponent(key: string): Component | undefined;
@@ -0,0 +1,23 @@
1
+ import type { ConditionFormState } from '@feedmepos/hrm-permission/condition-builder';
2
+ /**
3
+ * Pure decision extracted from `ConditionFormRenderer.vue`'s `setField`, so
4
+ * it can be unit-tested without mounting the component (this repo's vitest
5
+ * setup has no `@vue/test-utils` / jsdom-environment component-mounting
6
+ * infrastructure yet — see the composable spec's notes on jsdom gaps).
7
+ *
8
+ * Some `FmRadio` / `FmCheckbox` fields (ui-library) fire their change
9
+ * handler twice per click — the click handler lives on a `<label>` wrapping
10
+ * the native input, so one click fires the handler once directly and once
11
+ * via the browser's synthetic click bubbling back through the label — which
12
+ * would otherwise make `setField` emit `update:modelValue` twice with an
13
+ * identical value. Comparing the freshly-computed next state against the
14
+ * last emitted (or last committed) state collapses that into a single
15
+ * emission, while still emitting for any genuinely new value.
16
+ *
17
+ * Uses lodash `isEqual` (already this app's idiom for comparing plain
18
+ * records — see `AddMemberForm.vue`, `PermissionSetForm.vue`, `RoleForm.vue`)
19
+ * rather than a `JSON.stringify` comparison, since these flat
20
+ * `{ key: value }` form records can hold arrays (multiselect) and key order
21
+ * is not guaranteed to be stable across the spread in `setField`.
22
+ */
23
+ export declare function shouldEmitConditionFormUpdate(next: ConditionFormState, lastEmitted: ConditionFormState): boolean;