bitboss-ui 2.1.134 → 3.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +244 -21
- package/bin/bitboss-ui-mcp.mjs +557 -0
- package/bin/bitboss-ui.mjs +626 -0
- package/dist/ai/BbAccordion.md +522 -113
- package/dist/ai/BbAlert.md +377 -197
- package/dist/ai/BbAsterisk.md +46 -0
- package/dist/ai/BbAvatar.md +454 -178
- package/dist/ai/BbBadge.md +695 -225
- package/dist/ai/BbBadgeButton.md +54 -0
- package/dist/ai/BbBaseButton.md +489 -0
- package/dist/ai/BbBaseCheckbox.md +49 -0
- package/dist/ai/BbBaseCheckboxGroup.md +64 -0
- package/dist/ai/BbBaseCheckboxIcon.md +174 -0
- package/dist/ai/BbBaseColorInput.md +64 -0
- package/dist/ai/BbBaseColorPalette.md +39 -0
- package/dist/ai/BbBaseDatePicker.md +42 -0
- package/dist/ai/BbBaseDatePickerInput.md +73 -0
- package/dist/ai/BbBaseInputContainer.md +48 -0
- package/dist/ai/BbBaseNumberInput.md +71 -0
- package/dist/ai/BbBaseRadio.md +46 -0
- package/dist/ai/BbBaseRadioGroup.md +63 -0
- package/dist/ai/BbBaseRadioIcon.md +187 -0
- package/dist/ai/BbBaseRating.md +51 -0
- package/dist/ai/BbBaseSelect.md +88 -0
- package/dist/ai/BbBaseSlider.md +56 -0
- package/dist/ai/BbBaseSwitch.md +49 -0
- package/dist/ai/BbBaseSwitchGroup.md +64 -0
- package/dist/ai/BbBaseSwitchIcon.md +184 -0
- package/dist/ai/BbBaseTag.md +68 -0
- package/dist/ai/BbBaseTextInput.md +75 -0
- package/dist/ai/BbBaseTextarea.md +62 -0
- package/dist/ai/BbBaseTimePickerInput.md +62 -0
- package/dist/ai/BbBreadcrumbs.md +542 -503
- package/dist/ai/BbButton.md +767 -636
- package/dist/ai/BbCheckbox.md +572 -254
- package/dist/ai/BbCheckboxGroup.md +561 -361
- package/dist/ai/BbCollapsible.md +419 -103
- package/dist/ai/BbColorInput.md +529 -323
- package/dist/ai/BbColorPalette.md +565 -330
- package/dist/ai/BbConfirm.md +612 -136
- package/dist/ai/BbConfirmPortal.md +28 -0
- package/dist/ai/BbDatePicker.md +244 -0
- package/dist/ai/BbDatePickerInput.md +706 -388
- package/dist/ai/BbDialog.md +770 -99
- package/dist/ai/BbDropdown.md +1054 -763
- package/dist/ai/BbDropdownButton.md +511 -615
- package/dist/ai/BbDropdownGroup.md +34 -0
- package/dist/ai/BbDropzone.md +1148 -436
- package/dist/ai/BbForm.md +239 -0
- package/dist/ai/BbIcon.md +379 -212
- package/dist/ai/BbIndicator.md +283 -0
- package/dist/ai/BbNumberInput.md +713 -355
- package/dist/ai/BbOffCanvas.md +875 -498
- package/dist/ai/BbPagination.md +483 -535
- package/dist/ai/BbPopover.md +583 -546
- package/dist/ai/BbProgress.md +456 -69
- package/dist/ai/BbRadio.md +343 -229
- package/dist/ai/BbRadioGroup.md +426 -353
- package/dist/ai/BbRating.md +361 -226
- package/dist/ai/BbSelect.md +1380 -536
- package/dist/ai/BbSelectPopover.md +1240 -1579
- package/dist/ai/BbSlider.md +478 -257
- package/dist/ai/BbSmoothHeight.md +472 -139
- package/dist/ai/BbSpinner.md +718 -131
- package/dist/ai/BbSwitch.md +549 -119
- package/dist/ai/BbSwitchGroup.md +503 -214
- package/dist/ai/BbTable.md +1956 -2358
- package/dist/ai/BbTabs.md +1113 -0
- package/dist/ai/BbTabsList.md +29 -0
- package/dist/ai/BbTabsPanes.md +27 -0
- package/dist/ai/BbTabsRoot.md +57 -0
- package/dist/ai/BbTag.md +393 -292
- package/dist/ai/BbTextInput.md +852 -333
- package/dist/ai/BbTextarea.md +836 -255
- package/dist/ai/BbTimePicker.md +231 -0
- package/dist/ai/BbTimePickerInput.md +196 -0
- package/dist/ai/BbToast.md +624 -196
- package/dist/ai/BbToastPortal.md +28 -0
- package/dist/ai/BbTooltip.md +443 -346
- package/dist/ai/BbTree.md +984 -235
- package/dist/ai/ChipsBox.md +27 -199
- package/dist/ai/ClearableButton.md +21 -55
- package/dist/ai/CommaBox.md +25 -200
- package/dist/ai/CommonInputWrapper.md +42 -0
- package/dist/ai/CommonPopover.md +30 -510
- package/dist/ai/ErrorIcon.md +19 -56
- package/dist/ai/FlatListBox.md +35 -379
- package/dist/ai/GroupedListBox.md +37 -535
- package/dist/ai/GuidesComponent.md +21 -0
- package/dist/ai/ListBox.md +38 -231
- package/dist/ai/OptionsContainer.md +34 -253
- package/dist/ai/WarningIcon.md +21 -0
- package/dist/ai/components.json +15883 -0
- package/dist/ai/composables/useBbConfig.md +80 -0
- package/dist/ai/composables/useBbFormContext.md +110 -0
- package/dist/ai/composables/useBbTableContext.md +164 -0
- package/dist/ai/composables/useBbTabsContext.md +118 -0
- package/dist/ai/composables/useBroadcastChannelInstance.md +97 -0
- package/dist/ai/composables/useConfirm.md +306 -0
- package/dist/ai/composables/useCountdown.md +84 -0
- package/dist/ai/composables/useId.md +60 -0
- package/dist/ai/composables/useMobile.md +85 -0
- package/dist/ai/composables/useToast.md +230 -0
- package/dist/ai/guides/agent-contract.md +108 -0
- package/dist/ai/guides/ai-router.md +111 -0
- package/dist/ai/guides/app-layout.md +295 -0
- package/dist/ai/guides/coherence-playbook.md +136 -0
- package/dist/ai/guides/component-picker.md +195 -0
- package/dist/ai/guides/design-language.md +461 -0
- package/dist/ai/guides/design-tokens.md +424 -0
- package/dist/ai/guides/fetch-items-playbook.md +161 -0
- package/dist/ai/guides/floating-bottom-panel.md +191 -0
- package/dist/ai/guides/icons-policy.md +257 -0
- package/dist/ai/guides/inertia-helpers.md +1394 -0
- package/dist/ai/guides/inline-edit-playbook.md +258 -0
- package/dist/ai/guides/installation-and-plugin-setup.md +377 -0
- package/dist/ai/guides/migration/components/bb-alert.md +37 -0
- package/dist/ai/guides/migration/components/bb-avatar.md +24 -0
- package/dist/ai/guides/migration/components/bb-badge.md +110 -0
- package/dist/ai/guides/migration/components/bb-breadcrumbs.md +26 -0
- package/dist/ai/guides/migration/components/bb-button.md +55 -0
- package/dist/ai/guides/migration/components/bb-checkbox-group.md +54 -0
- package/dist/ai/guides/migration/components/bb-checkbox.md +45 -0
- package/dist/ai/guides/migration/components/bb-chip.md +35 -0
- package/dist/ai/guides/migration/components/bb-collapsible.md +26 -0
- package/dist/ai/guides/migration/components/bb-color-input.md +23 -0
- package/dist/ai/guides/migration/components/bb-color-palette.md +31 -0
- package/dist/ai/guides/migration/components/bb-date-picker-input.md +84 -0
- package/dist/ai/guides/migration/components/bb-dialog.md +46 -0
- package/dist/ai/guides/migration/components/bb-dropdown-button.md +31 -0
- package/dist/ai/guides/migration/components/bb-dropdown.md +36 -0
- package/dist/ai/guides/migration/components/bb-dropzone.md +27 -0
- package/dist/ai/guides/migration/components/bb-icon.md +27 -0
- package/dist/ai/guides/migration/components/bb-off-canvas.md +25 -0
- package/dist/ai/guides/migration/components/bb-offcanvas.md +54 -0
- package/dist/ai/guides/migration/components/bb-pagination.md +24 -0
- package/dist/ai/guides/migration/components/bb-popover.md +52 -0
- package/dist/ai/guides/migration/components/bb-rating.md +24 -0
- package/dist/ai/guides/migration/components/bb-select-popover.md +58 -0
- package/dist/ai/guides/migration/components/bb-select.md +68 -0
- package/dist/ai/guides/migration/components/bb-slider.md +25 -0
- package/dist/ai/guides/migration/components/bb-smooth-height.md +45 -0
- package/dist/ai/guides/migration/components/bb-spinner.md +23 -0
- package/dist/ai/guides/migration/components/bb-table.md +66 -0
- package/dist/ai/guides/migration/components/bb-tabs.md +47 -0
- package/dist/ai/guides/migration/components/bb-tag.md +29 -0
- package/dist/ai/guides/migration/components/bb-text-input.md +37 -0
- package/dist/ai/guides/migration/components/bb-toast.md +47 -0
- package/dist/ai/guides/migration/components/bb-tooltip.md +34 -0
- package/dist/ai/guides/migration/components/bb-tree.md +77 -0
- package/dist/ai/guides/migration/components/use-confirm.md +46 -0
- package/dist/ai/guides/migration/v2-to-v3.md +649 -0
- package/dist/ai/guides/options-items-playbook.md +222 -0
- package/dist/ai/guides/page-shell.md +314 -0
- package/dist/ai/guides/validated-forms.md +177 -0
- package/dist/ai/index.md +156 -32
- package/dist/ai/recipes/inertia/approvals-inbox.md +1598 -0
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +1733 -0
- package/dist/ai/recipes/inertia/onboarding.md +1765 -0
- package/dist/ai/recipes/inertia/ownership-atlas.md +1281 -0
- package/dist/ai/recipes/inertia/record-form.md +1442 -0
- package/dist/ai/recipes/inertia/records-workspace.md +1328 -0
- package/dist/ai/recipes/inertia/upload-center.md +1926 -0
- package/dist/ai/recipes/inertia/wizard-form.md +1477 -0
- package/dist/ai/recipes/nuxt/approvals-inbox.md +1561 -0
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +1690 -0
- package/dist/ai/recipes/nuxt/onboarding.md +1698 -0
- package/dist/ai/recipes/nuxt/record-form.md +1456 -0
- package/dist/ai/recipes/nuxt/records-workspace.md +1314 -0
- package/dist/ai/recipes/nuxt/upload-center.md +1847 -0
- package/dist/ai/recipes/nuxt/wizard-form.md +1486 -0
- package/dist/ai/recipes/vue/approvals-inbox.md +1479 -0
- package/dist/ai/recipes/vue/inline-edit-workspace.md +1581 -0
- package/dist/ai/recipes/vue/onboarding.md +1653 -0
- package/dist/ai/recipes/vue/record-form.md +1540 -0
- package/dist/ai/recipes/vue/records-workspace.md +1230 -0
- package/dist/ai/recipes/vue/upload-center.md +1806 -0
- package/dist/ai/recipes/vue/wizard-form.md +1418 -0
- package/dist/ai/source/BbAccordion.md +122 -0
- package/dist/ai/source/BbAlert.md +396 -0
- package/dist/ai/source/BbAsterisk.md +32 -0
- package/dist/ai/source/BbAvatar.md +256 -0
- package/dist/ai/source/BbBadge.md +560 -0
- package/dist/ai/source/BbBadgeButton.md +490 -0
- package/dist/ai/source/BbBaseButton.md +682 -0
- package/dist/ai/source/BbBaseCheckbox.md +459 -0
- package/dist/ai/source/BbBaseCheckboxGroup.md +554 -0
- package/dist/ai/source/BbBaseCheckboxIcon.md +201 -0
- package/dist/ai/source/BbBaseColorInput.md +413 -0
- package/dist/ai/source/BbBaseColorPalette.md +934 -0
- package/dist/ai/source/BbBaseDatePicker.md +1001 -0
- package/dist/ai/source/BbBaseDatePickerInput.md +1781 -0
- package/dist/ai/source/BbBaseInputContainer.md +1148 -0
- package/dist/ai/source/BbBaseNumberInput.md +615 -0
- package/dist/ai/source/BbBaseRadio.md +377 -0
- package/dist/ai/source/BbBaseRadioGroup.md +499 -0
- package/dist/ai/source/BbBaseRadioIcon.md +153 -0
- package/dist/ai/source/BbBaseRating.md +573 -0
- package/dist/ai/source/BbBaseSelect.md +895 -0
- package/dist/ai/source/BbBaseSlider.md +1085 -0
- package/dist/ai/source/BbBaseSwitch.md +246 -0
- package/dist/ai/source/BbBaseSwitchGroup.md +302 -0
- package/dist/ai/source/BbBaseSwitchIcon.md +206 -0
- package/dist/ai/source/BbBaseTag.md +688 -0
- package/dist/ai/source/BbBaseTextInput.md +511 -0
- package/dist/ai/source/BbBaseTextarea.md +480 -0
- package/dist/ai/source/BbBaseTimePickerInput.md +1013 -0
- package/dist/ai/source/BbBreadcrumbs.md +811 -0
- package/dist/ai/source/BbButton.md +990 -0
- package/dist/ai/source/BbCheckbox.md +319 -0
- package/dist/ai/source/BbCheckboxGroup.md +412 -0
- package/dist/ai/source/BbCollapsible.md +133 -0
- package/dist/ai/source/BbColorInput.md +315 -0
- package/dist/ai/source/BbColorPalette.md +361 -0
- package/dist/ai/source/BbConfirm.md +173 -0
- package/dist/ai/source/BbConfirmPortal.md +74 -0
- package/dist/ai/source/BbDatePicker.md +725 -0
- package/dist/ai/source/BbDatePickerInput.md +518 -0
- package/dist/ai/source/BbDialog.md +812 -0
- package/dist/ai/source/BbDropdown.md +2369 -0
- package/dist/ai/source/BbDropdownButton.md +313 -0
- package/dist/ai/source/BbDropdownGroup.md +1249 -0
- package/dist/ai/source/BbDropzone.md +749 -0
- package/dist/ai/source/BbForm.md +100 -0
- package/dist/ai/source/BbIcon.md +212 -0
- package/dist/ai/source/BbIndicator.md +298 -0
- package/dist/ai/source/BbNumberInput.md +365 -0
- package/dist/ai/source/BbOffCanvas.md +1260 -0
- package/dist/ai/source/BbPagination.md +829 -0
- package/dist/ai/source/BbPopover.md +853 -0
- package/dist/ai/source/BbProgress.md +140 -0
- package/dist/ai/source/BbRadio.md +294 -0
- package/dist/ai/source/BbRadioGroup.md +401 -0
- package/dist/ai/source/BbRating.md +362 -0
- package/dist/ai/source/BbSelect.md +681 -0
- package/dist/ai/source/BbSelectPopover.md +2209 -0
- package/dist/ai/source/BbSlider.md +371 -0
- package/dist/ai/source/BbSmoothHeight.md +261 -0
- package/dist/ai/source/BbSpinner.md +127 -0
- package/dist/ai/source/BbSwitch.md +175 -0
- package/dist/ai/source/BbSwitchGroup.md +240 -0
- package/dist/ai/source/BbTable.md +3348 -0
- package/dist/ai/source/BbTabs.md +403 -0
- package/dist/ai/source/BbTabsList.md +578 -0
- package/dist/ai/source/BbTabsPanes.md +368 -0
- package/dist/ai/source/BbTabsRoot.md +372 -0
- package/dist/ai/source/BbTag.md +383 -0
- package/dist/ai/source/BbTextInput.md +345 -0
- package/dist/ai/source/BbTextarea.md +330 -0
- package/dist/ai/source/BbTimePicker.md +591 -0
- package/dist/ai/source/BbTimePickerInput.md +367 -0
- package/dist/ai/source/BbToast.md +524 -0
- package/dist/ai/source/BbToastPortal.md +482 -0
- package/dist/ai/source/BbTooltip.md +508 -0
- package/dist/ai/source/BbTree.md +319 -0
- package/dist/ai/source/ChipsBox.md +201 -0
- package/dist/ai/source/ClearableButton.md +70 -0
- package/dist/ai/source/CommaBox.md +202 -0
- package/dist/ai/source/CommonInputWrapper.md +554 -0
- package/dist/ai/source/CommonPopover.md +606 -0
- package/dist/ai/source/ErrorIcon.md +43 -0
- package/dist/ai/source/FlatListBox.md +396 -0
- package/dist/ai/source/GroupedListBox.md +540 -0
- package/dist/ai/source/GuidesComponent.md +383 -0
- package/dist/ai/source/ListBox.md +238 -0
- package/dist/ai/source/OptionsContainer.md +191 -0
- package/dist/ai/source/WarningIcon.md +47 -0
- package/dist/alert-variants.d.ts +12 -0
- package/dist/assets/css/reset.css_inline.js +4 -0
- package/dist/assets/svgs/alert.svg_raw.js +4 -0
- package/dist/assets/svgs/calendar.svg_raw.js +4 -0
- package/dist/assets/svgs/check_stroke.svg_raw.js +4 -0
- package/dist/assets/svgs/chevron_down.svg_raw.js +4 -0
- package/dist/assets/svgs/chevron_right.svg_raw.js +4 -0
- package/dist/assets/svgs/clear.svg_raw.js +4 -0
- package/dist/assets/svgs/clock.svg_raw.js +4 -0
- package/dist/assets/svgs/error.svg_raw.js +4 -0
- package/dist/assets/svgs/search.svg_raw.js +4 -0
- package/dist/assets/svgs/sort.svg_raw.js +4 -0
- package/dist/assets/svgs/spinner.svg_raw.js +4 -0
- package/dist/assets/svgs/user.svg_raw.js +4 -0
- package/dist/badge-variants.d.ts +18 -0
- package/dist/button-variants.d.ts +15 -0
- package/dist/components/BbAccordion/BbAccordion.vue.d.ts +9 -3
- package/dist/components/BbAccordion/BbAccordion.vue.js +6 -0
- package/dist/components/BbAccordion/BbAccordion.vue_vue_type_script_setup_true_lang.js +66 -0
- package/dist/components/BbAccordion/types.d.ts +10 -7
- package/dist/components/BbAlert/BbAlert.vue.d.ts +12 -3
- package/dist/components/BbAlert/BbAlert.vue.js +6 -0
- package/dist/components/BbAlert/BbAlert.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/components/BbAlert/types.d.ts +51 -12
- package/dist/components/BbAsterisk/BbAsterisk.vue.d.ts +3 -0
- package/dist/components/BbAsterisk/BbAsterisk.vue.js +6 -0
- package/dist/components/BbAsterisk/BbAsterisk.vue_vue_type_script_setup_true_lang.js +13 -0
- package/dist/components/BbAvatar/BbAvatar.vue.d.ts +5 -0
- package/dist/components/BbAvatar/BbAvatar.vue.js +6 -0
- package/dist/components/BbAvatar/BbAvatar.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/components/BbAvatar/types.d.ts +4 -5
- package/dist/components/BbBadge/BadgeAppendIcon.vue.d.ts +3 -0
- package/dist/components/BbBadge/BadgeAppendIcon.vue.js +5 -0
- package/dist/components/BbBadge/BadgeAppendIcon.vue_vue_type_script_setup_true_lang.js +22 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue.d.ts +15 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue.js +5 -0
- package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +34 -0
- package/dist/components/BbBadge/BbBadge.vue.d.ts +8 -1
- package/dist/components/BbBadge/BbBadge.vue.js +6 -0
- package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/components/BbBadge/BbBadgeButton.vue.d.ts +12 -0
- package/dist/components/BbBadge/BbBadgeButton.vue.js +5 -0
- package/dist/components/BbBadge/BbBadgeButton.vue_vue_type_script_setup_true_lang.js +54 -0
- package/dist/components/BbBadge/badgeButtonContext.d.ts +27 -0
- package/dist/components/BbBadge/badgeButtonContext.js +5 -0
- package/dist/components/BbBadge/types.d.ts +61 -16
- package/dist/components/BbBaseButton/BbBaseButton.vue.d.ts +13 -0
- package/dist/components/BbBaseButton/BbBaseButton.vue.js +6 -0
- package/dist/components/BbBaseButton/BbBaseButton.vue_vue_type_script_setup_true_lang.js +171 -0
- package/dist/components/BbBaseButton/RouterComponent.vue.js +5 -0
- package/dist/components/BbBaseButton/RouterComponent.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/BbBaseButton/types.d.ts +191 -0
- package/dist/components/BbBaseButton/types.js +33 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue.d.ts +40 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue.js +6 -0
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue_vue_type_script_setup_true_lang.js +145 -0
- package/dist/components/BbBaseCheckbox/types.d.ts +222 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue.js +6 -0
- package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbBaseCheckboxGroup/types.d.ts +257 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue.js +6 -0
- package/dist/components/BbBaseCheckboxIcon/BbBaseCheckboxIcon.vue_vue_type_script_setup_true_lang.js +43 -0
- package/dist/components/BbBaseCheckboxIcon/types.d.ts +47 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue.d.ts +42 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue.js +6 -0
- package/dist/components/BbBaseColorInput/BbBaseColorInput.vue_vue_type_script_setup_true_lang.js +178 -0
- package/dist/components/BbBaseColorInput/types.d.ts +207 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue.d.ts +13 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue.js +6 -0
- package/dist/components/BbBaseColorPalette/BbBaseColorPalette.vue_vue_type_script_setup_true_lang.js +395 -0
- package/dist/components/BbBaseColorPalette/types.d.ts +40 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue.js +6 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +300 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.d.ts +44 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +310 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue.d.ts +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue.js +5 -0
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerYearSelector.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/components/BbBaseDatePicker/typeahead.d.ts +112 -0
- package/dist/components/BbBaseDatePicker/typeahead.js +37 -0
- package/dist/components/BbBaseDatePicker/types.d.ts +97 -0
- package/dist/components/BbBaseDatePicker/useDatePickerContext.d.ts +58 -0
- package/dist/components/BbBaseDatePicker/useDatePickerContext.js +11 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.d.ts +30 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.js +6 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +713 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue.d.ts +30 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue.js +6 -0
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerTimeSelector.vue_vue_type_script_setup_true_lang.js +204 -0
- package/dist/components/BbBaseDatePickerInput/types.d.ts +309 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.d.ts +17 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.js +6 -0
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbBaseInputContainer/types.d.ts +185 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue.d.ts +52 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue.js +6 -0
- package/dist/components/BbBaseNumberInput/BbBaseNumberInput.vue_vue_type_script_setup_true_lang.js +211 -0
- package/dist/components/BbBaseNumberInput/types.d.ts +211 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue.d.ts +33 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue.js +6 -0
- package/dist/components/BbBaseRadio/BbBaseRadio.vue_vue_type_script_setup_true_lang.js +114 -0
- package/dist/components/BbBaseRadio/types.d.ts +179 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue.js +6 -0
- package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue_vue_type_script_setup_true_lang.js +172 -0
- package/dist/components/BbBaseRadioGroup/types.d.ts +215 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue.js +6 -0
- package/dist/components/BbBaseRadioIcon/BbBaseRadioIcon.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/BbBaseRadioIcon/types.d.ts +41 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue.d.ts +38 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue.js +6 -0
- package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbBaseRating/types.d.ts +168 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue.d.ts +19 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue.js +6 -0
- package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +307 -0
- package/dist/components/BbBaseSelect/types.d.ts +294 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue.d.ts +35 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue.js +6 -0
- package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +324 -0
- package/dist/components/BbBaseSlider/types.d.ts +200 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue.d.ts +35 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue.js +6 -0
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue_vue_type_script_setup_true_lang.js +146 -0
- package/dist/components/BbBaseSwitch/types.d.ts +10 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue.d.ts +19 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue.js +6 -0
- package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbBaseSwitchGroup/types.d.ts +8 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue.d.ts +4 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue.js +6 -0
- package/dist/components/BbBaseSwitchIcon/BbBaseSwitchIcon.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/BbBaseSwitchIcon/types.d.ts +48 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue.d.ts +50 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue.js +6 -0
- package/dist/components/BbBaseTag/BbBaseTag.vue_vue_type_script_setup_true_lang.js +266 -0
- package/dist/components/BbBaseTag/types.d.ts +160 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue.d.ts +45 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue.js +6 -0
- package/dist/components/BbBaseTextInput/BbBaseTextInput.vue_vue_type_script_setup_true_lang.js +200 -0
- package/dist/components/BbBaseTextInput/types.d.ts +209 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue.d.ts +39 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue.js +6 -0
- package/dist/components/BbBaseTextarea/BbBaseTextarea.vue_vue_type_script_setup_true_lang.js +175 -0
- package/dist/components/BbBaseTextarea/types.d.ts +170 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.d.ts +27 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.js +6 -0
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +517 -0
- package/dist/components/BbBaseTimePickerInput/types.d.ts +151 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.d.ts +0 -1
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue.js +6 -0
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +224 -0
- package/dist/components/BbBreadcrumbs/estimateBreadcrumbItemWidth.js +47 -0
- package/dist/components/BbBreadcrumbs/types.d.ts +33 -22
- package/dist/components/BbButton/BbButton.vue.d.ts +6 -5
- package/dist/components/BbButton/BbButton.vue.js +6 -0
- package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +177 -0
- package/dist/components/BbButton/types.d.ts +48 -47
- package/dist/components/BbCheckbox/BbCheckbox.vue.d.ts +4 -3
- package/dist/components/BbCheckbox/BbCheckbox.vue.js +6 -0
- package/dist/components/BbCheckbox/BbCheckbox.vue_vue_type_script_setup_true_lang.js +146 -0
- package/dist/components/BbCheckbox/types.d.ts +24 -10
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.d.ts +2 -13
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue.js +6 -0
- package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbCheckboxGroup/types.d.ts +44 -23
- package/dist/components/BbCollapsible/BbCollapsible.vue.d.ts +2 -1
- package/dist/components/BbCollapsible/BbCollapsible.vue.js +6 -0
- package/dist/components/BbCollapsible/BbCollapsible.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/components/BbCollapsible/types.d.ts +10 -9
- package/dist/components/BbColorInput/BbColorInput.vue.d.ts +10 -1
- package/dist/components/BbColorInput/BbColorInput.vue.js +6 -0
- package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +177 -0
- package/dist/components/BbColorInput/types.d.ts +37 -11
- package/dist/components/BbColorPalette/BbColorPalette.vue.d.ts +5 -8
- package/dist/components/BbColorPalette/BbColorPalette.vue.js +5 -0
- package/dist/components/BbColorPalette/BbColorPalette.vue_vue_type_script_setup_true_lang.js +125 -0
- package/dist/components/BbColorPalette/types.d.ts +66 -53
- package/dist/components/BbConfirm/BbConfirm.vue.js +6 -0
- package/dist/components/BbConfirm/BbConfirm.vue_vue_type_script_setup_true_lang.js +130 -0
- package/dist/components/BbConfirm/BbConfirmPortal.vue.d.ts +5 -6
- package/dist/components/BbConfirm/BbConfirmPortal.vue.js +5 -0
- package/dist/components/BbConfirm/BbConfirmPortal.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue.d.ts +37 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue.js +6 -0
- package/dist/components/BbDatePicker/BbDatePicker.vue_vue_type_script_setup_true_lang.js +319 -0
- package/dist/components/BbDatePicker/types.d.ts +155 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.d.ts +3 -1
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.js +6 -0
- package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +179 -0
- package/dist/components/BbDatePickerInput/types.d.ts +127 -16
- package/dist/components/BbDialog/BbDialog.vue.d.ts +8 -6
- package/dist/components/BbDialog/BbDialog.vue.js +6 -0
- package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +214 -0
- package/dist/components/BbDialog/types.d.ts +146 -1
- package/dist/components/BbDialog/useDialogStack.d.ts +5 -0
- package/dist/components/BbDialog/useDialogStack.js +20 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue.d.ts +18 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue.js +6 -0
- package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +116 -0
- package/dist/components/BbDropdown/BbDropdown.vue.d.ts +14 -7
- package/dist/components/BbDropdown/BbDropdown.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +560 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue.d.ts +44 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue.js +5 -0
- package/dist/components/BbDropdown/BbDropdownGroup.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.d.ts +27 -0
- package/dist/components/BbDropdown/BbDropdownList.vue.js +6 -0
- package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +464 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue.d.ts +16 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue.js +5 -0
- package/dist/components/BbDropdown/DropdownPipelineResolver.vue_vue_type_script_setup_true_lang.js +168 -0
- package/dist/components/BbDropdown/normalizeGroups.d.ts +18 -0
- package/dist/components/BbDropdown/normalizeGroups.js +46 -0
- package/dist/components/BbDropdown/types.d.ts +457 -45
- package/dist/components/BbDropdown/types.js +4 -0
- package/dist/components/BbDropdown/useDropdownContext.d.ts +89 -0
- package/dist/components/BbDropdown/useDropdownContext.js +9 -0
- package/dist/components/BbDropdown/useDropdownGroupRegistry.d.ts +108 -0
- package/dist/components/BbDropdown/useDropdownGroupRegistry.js +5 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.d.ts +3 -3
- package/dist/components/BbDropdownButton/BbDropdownButton.vue.js +6 -0
- package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +119 -0
- package/dist/components/BbDropdownButton/types.d.ts +45 -52
- package/dist/components/BbDropzone/BbDropzone.vue.d.ts +5 -4
- package/dist/components/BbDropzone/BbDropzone.vue.js +6 -0
- package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +284 -0
- package/dist/components/BbDropzone/types.d.ts +55 -7
- package/dist/components/BbIcon/BbIcon.vue.d.ts +3 -2
- package/dist/components/BbIcon/BbIcon.vue.js +6 -0
- package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/components/BbIcon/types.d.ts +6 -19
- package/dist/components/BbIndicator/BbIndicator.vue.d.ts +14 -0
- package/dist/components/BbIndicator/BbIndicator.vue.js +6 -0
- package/dist/components/BbIndicator/BbIndicator.vue_vue_type_script_setup_true_lang.js +56 -0
- package/dist/components/BbIndicator/types.d.ts +49 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue.d.ts +12 -6
- package/dist/components/BbNumberInput/BbNumberInput.vue.js +6 -0
- package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +194 -0
- package/dist/components/BbNumberInput/types.d.ts +31 -4
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +10 -10
- package/dist/components/BbOffCanvas/BbOffCanvas.vue.js +6 -0
- package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +284 -0
- package/dist/components/BbOffCanvas/types.d.ts +71 -39
- package/dist/components/BbOffCanvas/useOffCanvasStack.d.ts +25 -0
- package/dist/components/BbOffCanvas/useOffCanvasStack.js +68 -0
- package/dist/components/BbPagination/BbPagination.vue.d.ts +3 -4
- package/dist/components/BbPagination/BbPagination.vue.js +6 -0
- package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +244 -0
- package/dist/components/BbPagination/paginationFit.d.ts +38 -0
- package/dist/components/BbPagination/paginationFit.js +13 -0
- package/dist/components/BbPagination/types.d.ts +51 -16
- package/dist/components/BbPopover/BbPopover.vue.d.ts +10 -15
- package/dist/components/BbPopover/BbPopover.vue.js +6 -0
- package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +310 -0
- package/dist/components/BbPopover/escapeStack.d.ts +6 -0
- package/dist/components/BbPopover/escapeStack.js +10 -0
- package/dist/components/BbPopover/types.d.ts +84 -31
- package/dist/components/BbProgress/BbProgress.vue.d.ts +1 -0
- package/dist/components/BbProgress/BbProgress.vue.js +6 -0
- package/dist/components/BbProgress/BbProgress.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/BbProgress/types.d.ts +15 -1
- package/dist/components/BbRadio/BbRadio.vue.d.ts +5 -3
- package/dist/components/BbRadio/BbRadio.vue.js +6 -0
- package/dist/components/BbRadio/BbRadio.vue_vue_type_script_setup_true_lang.js +133 -0
- package/dist/components/BbRadio/types.d.ts +23 -9
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.d.ts +4 -14
- package/dist/components/BbRadioGroup/BbRadioGroup.vue.js +6 -0
- package/dist/components/BbRadioGroup/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/components/BbRadioGroup/types.d.ts +41 -22
- package/dist/components/BbRating/BbRating.vue.d.ts +4 -1
- package/dist/components/BbRating/BbRating.vue.js +6 -0
- package/dist/components/BbRating/BbRating.vue_vue_type_script_setup_true_lang.js +150 -0
- package/dist/components/BbRating/types.d.ts +37 -10
- package/dist/components/BbSelect/BbSelect.vue.d.ts +2 -11
- package/dist/components/BbSelect/BbSelect.vue.js +6 -0
- package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +241 -0
- package/dist/components/BbSelect/types.d.ts +105 -59
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.d.ts +1 -10
- package/dist/components/BbSelectPopover/BbSelectPopover.vue.js +6 -0
- package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +637 -0
- package/dist/components/BbSelectPopover/types.d.ts +189 -102
- package/dist/components/BbSlider/BbSlider.vue.d.ts +3 -6
- package/dist/components/BbSlider/BbSlider.vue.js +6 -0
- package/dist/components/BbSlider/BbSlider.vue_vue_type_script_setup_true_lang.js +166 -0
- package/dist/components/BbSlider/types.d.ts +37 -13
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.d.ts +3 -1
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.js +6 -0
- package/dist/components/BbSmoothHeight/BbSmoothHeight.vue_vue_type_script_setup_true_lang.js +79 -0
- package/dist/components/BbSmoothHeight/types.d.ts +13 -2
- package/dist/components/BbSpinner/BbSpinner.vue.d.ts +1 -1
- package/dist/components/BbSpinner/BbSpinner.vue.js +6 -0
- package/dist/components/BbSpinner/BbSpinner.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/BbSpinner/types.d.ts +10 -3
- package/dist/components/BbSwitch/BbSwitch.vue.d.ts +5 -3
- package/dist/components/BbSwitch/BbSwitch.vue.js +6 -0
- package/dist/components/BbSwitch/BbSwitch.vue_vue_type_script_setup_true_lang.js +142 -0
- package/dist/components/BbSwitch/types.d.ts +3 -1
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.d.ts +4 -14
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue.js +6 -0
- package/dist/components/BbSwitchGroup/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +190 -0
- package/dist/components/BbSwitchGroup/types.d.ts +3 -22
- package/dist/components/BbTable/BbTable.vue.d.ts +53 -31
- package/dist/components/BbTable/BbTable.vue.js +6 -0
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +889 -0
- package/dist/components/BbTable/types.d.ts +176 -37
- package/dist/components/BbTable/utils.d.ts +9 -1
- package/dist/components/BbTable/utils.js +53 -0
- package/dist/components/BbTabs/BbTabs.vue.d.ts +20 -0
- package/dist/components/BbTabs/BbTabs.vue.js +6 -0
- package/dist/components/BbTabs/BbTabs.vue_vue_type_script_setup_true_lang.js +64 -0
- package/dist/components/BbTabs/BbTabsList.vue.d.ts +19 -0
- package/dist/components/BbTabs/BbTabsList.vue.js +6 -0
- package/dist/components/BbTabs/BbTabsList.vue_vue_type_script_setup_true_lang.js +123 -0
- package/dist/components/BbTabs/BbTabsPanes.vue.d.ts +16 -0
- package/dist/components/BbTabs/BbTabsPanes.vue.js +6 -0
- package/dist/components/BbTabs/BbTabsPanes.vue_vue_type_script_setup_true_lang.js +35 -0
- package/dist/components/BbTabs/BbTabsRoot.vue.d.ts +20 -0
- package/dist/components/BbTabs/BbTabsRoot.vue.js +5 -0
- package/dist/components/BbTabs/BbTabsRoot.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/BbTabs/types.d.ts +264 -0
- package/dist/components/BbTag/BbTag.vue.d.ts +19 -6
- package/dist/components/BbTag/BbTag.vue.js +6 -0
- package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +187 -0
- package/dist/components/BbTag/types.d.ts +48 -17
- package/dist/components/BbTextInput/BbTextInput.vue.d.ts +10 -1
- package/dist/components/BbTextInput/BbTextInput.vue.js +6 -0
- package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +195 -0
- package/dist/components/BbTextInput/types.d.ts +24 -4
- package/dist/components/BbTextarea/BbTextarea.vue.d.ts +10 -1
- package/dist/components/BbTextarea/BbTextarea.vue.js +6 -0
- package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +178 -0
- package/dist/components/BbTextarea/types.d.ts +26 -2
- package/dist/components/BbTimePicker/BbTimePicker.vue.d.ts +33 -0
- package/dist/components/BbTimePicker/BbTimePicker.vue.js +6 -0
- package/dist/components/BbTimePicker/BbTimePicker.vue_vue_type_script_setup_true_lang.js +244 -0
- package/dist/components/BbTimePicker/types.d.ts +129 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue.d.ts +25 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue.js +6 -0
- package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +157 -0
- package/dist/components/BbTimePickerInput/types.d.ts +165 -0
- package/dist/components/BbToast/BbToast.vue.d.ts +2 -3
- package/dist/components/BbToast/BbToast.vue.js +6 -0
- package/dist/components/BbToast/BbToast.vue_vue_type_script_setup_true_lang.js +35 -0
- package/dist/components/BbToast/BbToastMessage.vue.d.ts +17 -8
- package/dist/components/BbToast/BbToastMessage.vue.js +5 -0
- package/dist/components/BbToast/BbToastMessage.vue_vue_type_script_setup_true_lang.js +170 -0
- package/dist/components/BbToast/BbToastPortal.vue.d.ts +30 -0
- package/dist/components/BbToast/BbToastPortal.vue.js +5 -0
- package/dist/components/BbToast/BbToastPortal.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/BbToast/BbToastRegion.vue.d.ts +8 -0
- package/dist/components/BbToast/BbToastRegion.vue.js +5 -0
- package/dist/components/BbToast/BbToastRegion.vue_vue_type_script_setup_true_lang.js +100 -0
- package/dist/components/BbToast/types.d.ts +69 -5
- package/dist/components/BbTooltip/BbTooltip.vue.d.ts +4 -3
- package/dist/components/BbTooltip/BbTooltip.vue.js +6 -0
- package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +160 -0
- package/dist/components/BbTooltip/types.d.ts +61 -11
- package/dist/components/BbTree/BbTree.vue.d.ts +26 -13
- package/dist/components/BbTree/BbTree.vue.js +6 -0
- package/dist/components/BbTree/BbTree.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/BbTree/BbTreeLevel.vue.d.ts +19 -0
- package/dist/components/BbTree/BbTreeLevel.vue.js +5 -0
- package/dist/components/BbTree/BbTreeLevel.vue_vue_type_script_setup_true_lang.js +94 -0
- package/dist/components/BbTree/context.d.ts +17 -0
- package/dist/components/BbTree/context.js +4 -0
- package/dist/components/BbTree/types.d.ts +93 -42
- package/dist/components/ChipsBox/ChipsBox.vue.d.ts +2 -1
- package/dist/components/ChipsBox/ChipsBox.vue.js +5 -0
- package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +98 -0
- package/dist/components/ChipsBox/types.d.ts +12 -5
- package/dist/components/ClearableButton/ClearableButton.vue.d.ts +2 -1
- package/dist/components/ClearableButton/ClearableButton.vue.js +6 -0
- package/dist/components/ClearableButton/ClearableButton.vue_vue_type_script_setup_true_lang.js +19 -0
- package/dist/components/ClearableButton/types.d.ts +7 -1
- package/dist/components/CloseIcon/CloseButton.vue.d.ts +5 -0
- package/dist/components/CloseIcon/CloseButton.vue.js +7 -0
- package/dist/components/CloseIcon/CloseButton.vue_vue_type_script_setup_true_lang.js +23 -0
- package/dist/components/CloseIcon/types.d.ts +2 -0
- package/dist/components/CommaBox/CommaBox.vue.d.ts +2 -1
- package/dist/components/CommaBox/CommaBox.vue.js +6 -0
- package/dist/components/CommaBox/CommaBox.vue_vue_type_script_setup_true_lang.js +81 -0
- package/dist/components/CommaBox/types.d.ts +6 -7
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.d.ts +19 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue.js +6 -0
- package/dist/components/CommonInputWrapper/CommonInputWrapper.vue_vue_type_script_setup_true_lang.js +107 -0
- package/dist/components/CommonInputWrapper/types.d.ts +96 -0
- package/dist/components/CommonPopover/CommonPopover.vue.d.ts +3 -3
- package/dist/components/CommonPopover/CommonPopover.vue.js +6 -0
- package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +160 -0
- package/dist/components/CommonPopover/types.d.ts +61 -16
- package/dist/components/ErrorIcon/ErrorIcon.vue.d.ts +2 -0
- package/dist/components/ErrorIcon/ErrorIcon.vue.js +6 -0
- package/dist/components/ErrorIcon/ErrorIcon.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/FlatListBox/FlatListBox.vue.d.ts +2 -5
- package/dist/components/FlatListBox/FlatListBox.vue.js +5 -0
- package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +167 -0
- package/dist/components/FlatListBox/types.d.ts +1 -1
- package/dist/components/GroupedListBox/GroupedListBox.vue.d.ts +2 -5
- package/dist/components/GroupedListBox/GroupedListBox.vue.js +5 -0
- package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +218 -0
- package/dist/components/ListBox/ListBox.vue.d.ts +3 -6
- package/dist/components/ListBox/ListBox.vue.js +5 -0
- package/dist/components/ListBox/ListBox.vue_vue_type_script_setup_true_lang.js +112 -0
- package/dist/components/ListBox/types.d.ts +1 -1
- package/dist/components/OptionsContainer/OptionsContainer.vue.d.ts +1 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue.js +5 -0
- package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/components/OptionsContainer/types.d.ts +14 -24
- package/dist/components/Transitions/ScaleFade.vue.js +5 -0
- package/dist/components/Transitions/ScaleFade.vue_vue_type_script_setup_true_lang.js +47 -0
- package/dist/components/Transitions/Slide.vue.js +7 -0
- package/dist/components/Transitions/Slide.vue_vue_type_script_setup_true_lang.js +28 -0
- package/dist/components/WarningIcon/WarningIcon.vue.d.ts +5 -0
- package/dist/components/WarningIcon/WarningIcon.vue.js +6 -0
- package/dist/components/WarningIcon/WarningIcon.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/components/WarningIcon/types.d.ts +3 -0
- package/dist/composables/__testResets.d.ts +1 -0
- package/dist/composables/useActiveInactiveState.d.ts +4 -0
- package/dist/composables/useActiveInactiveState.js +21 -0
- package/dist/composables/useBaseOptions.d.ts +3 -3
- package/dist/composables/useBaseOptions.js +31 -0
- package/dist/composables/useBbConfig.d.ts +8 -37
- package/dist/composables/useBbConfig.js +5 -0
- package/dist/composables/useBbDropdownContext.d.ts +89 -0
- package/dist/composables/useBbDropdownContext.js +46 -0
- package/dist/composables/useBbFormContext.d.ts +90 -0
- package/dist/composables/useBbFormContext.js +69 -0
- package/dist/composables/useBbTableContext.d.ts +195 -0
- package/dist/composables/useBbTableContext.js +208 -0
- package/dist/composables/useBbTabsContext.d.ts +111 -0
- package/dist/composables/useBbTabsContext.js +219 -0
- package/dist/composables/useBreakpoint.d.ts +13 -0
- package/dist/composables/useBreakpoint.js +39 -0
- package/dist/composables/useBroadcastChannelInstance.d.ts +1 -3
- package/dist/composables/useBroadcastChannelInstance.js +39 -0
- package/dist/composables/useCancelableFunction.js +37 -0
- package/dist/composables/useCoherence.js +16 -0
- package/dist/composables/useConfirm.d.ts +257 -67
- package/dist/composables/useConfirm.js +122 -0
- package/dist/composables/useCountdown.js +42 -0
- package/dist/composables/useHashedWatcher.d.ts +17 -0
- package/dist/composables/useHashedWatcher.js +14 -0
- package/dist/composables/useId.d.ts +3 -2
- package/dist/composables/useIndexById.js +16 -0
- package/dist/composables/useInertiaPage.d.ts +10 -0
- package/dist/composables/useInertiaPage.js +8 -0
- package/dist/composables/useItemText.js +17 -0
- package/dist/composables/useItemValue.js +9 -0
- package/dist/composables/useItemsGetter.d.ts +1 -1
- package/dist/composables/useItemsGetter.js +30 -0
- package/dist/composables/useListboxFocus.js +66 -0
- package/dist/composables/useLocale.d.ts +1 -1
- package/dist/composables/useLocale.js +11 -0
- package/dist/composables/useLogger.d.ts +1 -1
- package/dist/composables/useLogger.js +10 -0
- package/dist/composables/useMinimalQueue.d.ts +1 -1
- package/dist/composables/useMobile.js +7 -0
- package/dist/composables/useOnFalsy.d.ts +2 -0
- package/dist/composables/useOnFalsy.js +9 -0
- package/dist/composables/useOnTruthy.d.ts +7 -0
- package/dist/composables/useOnTruthy.js +9 -0
- package/dist/composables/usePrefill.d.ts +2 -1
- package/dist/composables/usePrefill.js +24 -0
- package/dist/composables/useQuery.d.ts +15 -3
- package/dist/composables/useQuery.js +42 -0
- package/dist/composables/useRoute.js +11 -0
- package/dist/composables/useRouter.js +8 -0
- package/dist/composables/useSafeArea.d.ts +51 -0
- package/dist/composables/useSafeArea.js +16 -0
- package/dist/composables/useSegmentedFields.d.ts +103 -0
- package/dist/composables/useSegmentedFields.js +152 -0
- package/dist/composables/useTableWidthContext.d.ts +5 -0
- package/dist/composables/useTableWidthContext.js +42 -0
- package/dist/composables/useToast.d.ts +17 -73
- package/dist/composables/useToast.js +49 -0
- package/dist/composables/useTopLayer.d.ts +9 -0
- package/dist/composables/useTopLayer.js +7 -0
- package/dist/composables/useUntil.js +13 -0
- package/dist/composables/useViewportWidth.d.ts +17 -0
- package/dist/composables/useViewportWidth.js +6 -0
- package/dist/confirm-variants.d.ts +10 -0
- package/dist/deprecation/ai-deprecations.json.d.ts +784 -0
- package/dist/deprecation/ai-deprecations.json.js +4 -0
- package/dist/deprecation/removedProps.d.ts +3 -0
- package/dist/deprecation/removedProps.js +13 -0
- package/dist/directives/bbColor.d.ts +65 -0
- package/dist/directives/bbColor.js +24 -0
- package/dist/directives/bbDate.d.ts +66 -0
- package/dist/directives/bbDate.js +24 -0
- package/dist/directives/bbDropdown.d.ts +15 -11
- package/dist/directives/bbDropdown.js +23 -0
- package/dist/directives/bbTime.d.ts +64 -0
- package/dist/directives/bbTime.js +24 -0
- package/dist/directives/bbTooltip.d.ts +16 -11
- package/dist/directives/bbTooltip.js +24 -0
- package/dist/dropdown-item-variants.d.ts +10 -0
- package/dist/i18n/index.d.ts +51 -3
- package/dist/i18n/index.js +27 -0
- package/dist/i18n/locales/cs.json +115 -0
- package/dist/i18n/locales/da.json +115 -0
- package/dist/i18n/locales/de.json +115 -0
- package/dist/i18n/locales/el.json +115 -0
- package/dist/i18n/locales/en.json +115 -0
- package/dist/i18n/locales/es.json +115 -0
- package/dist/i18n/locales/fi.json +115 -0
- package/dist/i18n/locales/fr.json +115 -0
- package/dist/i18n/locales/hu.json +115 -0
- package/dist/i18n/locales/it.json +115 -0
- package/dist/i18n/locales/ja.json +115 -0
- package/dist/i18n/locales/ko.json +115 -0
- package/dist/i18n/locales/nb.json +115 -0
- package/dist/i18n/locales/nl.json +115 -0
- package/dist/i18n/locales/pl.json +115 -0
- package/dist/i18n/locales/pt.json +115 -0
- package/dist/i18n/locales/ro.json +115 -0
- package/dist/i18n/locales/ru.json +115 -0
- package/dist/i18n/locales/sv.json +115 -0
- package/dist/i18n/locales/tr.json +115 -0
- package/dist/i18n/locales/uk.json +115 -0
- package/dist/i18n/locales/zh-cn.json +115 -0
- package/dist/icons-whitelist.d.ts +8 -0
- package/dist/index.d.ts +94 -79
- package/dist/index.js +76 -82
- package/dist/indicator-variants.d.ts +13 -0
- package/dist/llms-full.txt +77073 -0
- package/dist/llms-medium.txt +1334 -0
- package/dist/llms.txt +103 -0
- package/dist/locale-blueprint.json +141 -0
- package/dist/nuxt-module.d.ts +3 -0
- package/dist/nuxt.js +19 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.js +22 -0
- package/dist/reset.css +1 -0
- package/dist/reset.d.ts +0 -0
- package/dist/runtime/nuxt-plugin.d.ts +2 -0
- package/dist/runtime/nuxt-plugin.js +24 -0
- package/dist/styles.css +2 -0
- package/dist/styles.d.ts +0 -0
- package/dist/toast-variants.d.ts +13 -0
- package/dist/tooltip-variants.d.ts +10 -0
- package/dist/types/ActionButton.d.ts +32 -0
- package/dist/types/AlertVariant.d.ts +16 -0
- package/dist/types/BadgeVariant.d.ts +16 -0
- package/dist/types/ButtonVariant.d.ts +17 -0
- package/dist/types/Classes.d.ts +1 -1
- package/dist/types/ColumnName.d.ts +1 -1
- package/dist/types/CommonProps.d.ts +66 -279
- package/dist/types/Config.d.ts +84 -0
- package/dist/types/ConfirmVariant.d.ts +15 -0
- package/dist/types/DropdownItemVariant.d.ts +18 -0
- package/dist/types/Icon.d.ts +5 -7
- package/dist/types/IndicatorVariant.d.ts +16 -0
- package/dist/types/InputDirection.d.ts +15 -0
- package/dist/types/ItemAccessor.d.ts +21 -0
- package/dist/types/Placement.d.ts +6 -0
- package/dist/types/ResponsiveSize.d.ts +20 -0
- package/dist/types/SizeScale.d.ts +8 -0
- package/dist/types/ToastVariant.d.ts +17 -0
- package/dist/types/TooltipVariant.d.ts +18 -0
- package/dist/utilities/functions/bytesToUnit.d.ts +2 -0
- package/dist/utilities/functions/bytesToUnit.js +18 -0
- package/dist/utilities/functions/clampPrecision.js +10 -0
- package/dist/utilities/functions/convertDateValue.d.ts +23 -10
- package/dist/utilities/functions/convertDateValue.js +12 -0
- package/dist/utilities/functions/deburr.js +5 -0
- package/dist/utilities/functions/empty.js +5 -0
- package/dist/utilities/functions/flattenTree.d.ts +1 -1
- package/dist/utilities/functions/hash.js +5 -0
- package/dist/utilities/functions/isDateDisabled.d.ts +1 -0
- package/dist/utilities/functions/isDateDisabled.js +20 -0
- package/dist/utilities/functions/isEqual.js +5 -0
- package/dist/utilities/functions/isNotEmpty.js +5 -0
- package/dist/utilities/functions/matchAnyKey.d.ts +1 -1
- package/dist/utilities/functions/matchAnyKey.js +15 -0
- package/dist/utilities/functions/parseSize.d.ts +1 -1
- package/dist/utilities/functions/parseSize.js +15 -0
- package/dist/utilities/functions/percentage.js +11 -0
- package/dist/utilities/functions/pipe.d.ts +1 -1
- package/dist/utilities/functions/reject.d.ts +25 -0
- package/dist/utilities/functions/reject.js +28 -0
- package/dist/utilities/functions/resolveEnumProp.d.ts +19 -0
- package/dist/utilities/functions/resolveEnumProp.js +5 -0
- package/dist/utilities/functions/slotKey.d.ts +13 -0
- package/dist/utilities/functions/slotKey.js +4 -0
- package/dist/utilities/functions/sort.d.ts +2 -2
- package/dist/utilities/functions/sort.js +19 -0
- package/dist/utilities/functions/throttle.js +5 -0
- package/dist/utilities/functions/waitFor.d.ts +9 -2
- package/dist/utilities/functions/waitFor.js +14 -0
- package/dist/utilities/functions/warnRemovedAttrs.d.ts +11 -0
- package/dist/utilities/functions/warnRemovedAttrs.js +20 -0
- package/dist/utilities/functions/when.js +5 -0
- package/dist/utilities/misc/iconsInjectionKey.d.ts +15 -0
- package/dist/utilities/misc/iconsInjectionKey.js +4 -0
- package/dist/utilities/misc/transitions.d.ts +5 -0
- package/dist/utilities/misc/transitions.js +8 -0
- package/dist/utils/injectLibraryStyles.d.ts +6 -0
- package/dist/utils/injectLibraryStyles.js +38 -0
- package/dist/validated/BbCheckbox.vue.d.ts +29 -0
- package/dist/validated/BbCheckbox.vue.js +5 -0
- package/dist/validated/BbCheckbox.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbCheckboxGroup.vue.d.ts +34 -0
- package/dist/validated/BbCheckboxGroup.vue.js +5 -0
- package/dist/validated/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +76 -0
- package/dist/validated/BbColorInput.vue.d.ts +36 -0
- package/dist/validated/BbColorInput.vue.js +5 -0
- package/dist/validated/BbColorInput.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/validated/BbDatePickerInput.vue.d.ts +40 -0
- package/dist/validated/BbDatePickerInput.vue.js +5 -0
- package/dist/validated/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +87 -0
- package/dist/validated/BbDropzone.vue.d.ts +24 -0
- package/dist/validated/BbDropzone.vue.js +5 -0
- package/dist/validated/BbDropzone.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/validated/BbForm.vue.d.ts +28 -0
- package/dist/validated/BbForm.vue.js +5 -0
- package/dist/validated/BbForm.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/validated/BbNumberInput.vue.d.ts +36 -0
- package/dist/validated/BbNumberInput.vue.js +5 -0
- package/dist/validated/BbNumberInput.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbRadioGroup.vue.d.ts +34 -0
- package/dist/validated/BbRadioGroup.vue.js +5 -0
- package/dist/validated/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbRating.vue.d.ts +30 -0
- package/dist/validated/BbRating.vue.js +5 -0
- package/dist/validated/BbRating.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/validated/BbSelect.vue.d.ts +46 -0
- package/dist/validated/BbSelect.vue.js +5 -0
- package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +100 -0
- package/dist/validated/BbSlider.vue.d.ts +36 -0
- package/dist/validated/BbSlider.vue.js +5 -0
- package/dist/validated/BbSlider.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/validated/BbSwitch.vue.d.ts +29 -0
- package/dist/validated/BbSwitch.vue.js +5 -0
- package/dist/validated/BbSwitch.vue_vue_type_script_setup_true_lang.js +75 -0
- package/dist/validated/BbSwitchGroup.vue.d.ts +34 -0
- package/dist/validated/BbSwitchGroup.vue.js +5 -0
- package/dist/validated/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +76 -0
- package/dist/validated/BbTag.vue.d.ts +36 -0
- package/dist/validated/BbTag.vue.js +5 -0
- package/dist/validated/BbTag.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbTextInput.vue.d.ts +36 -0
- package/dist/validated/BbTextInput.vue.js +5 -0
- package/dist/validated/BbTextInput.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/validated/BbTextarea.vue.d.ts +36 -0
- package/dist/validated/BbTextarea.vue.js +5 -0
- package/dist/validated/BbTextarea.vue_vue_type_script_setup_true_lang.js +70 -0
- package/dist/validated/BbTimePickerInput.vue.d.ts +36 -0
- package/dist/validated/BbTimePickerInput.vue.js +5 -0
- package/dist/validated/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +78 -0
- package/dist/validated/index.d.ts +29 -0
- package/dist/validated/useValidatedField.d.ts +52 -0
- package/dist/validated/useValidatedField.js +51 -0
- package/dist/validated.js +21 -0
- package/dist/vite-plugin.d.ts +217 -0
- package/dist/vite.js +530 -0
- package/llms.txt +105 -0
- package/package.json +161 -59
- package/scripts/lib/__fixtures__/validated-manifest.json +65 -0
- package/scripts/lib/ensure-manifest.mjs +39 -0
- package/scripts/lib/eslint-plugin-autofix.node.test.mjs +109 -0
- package/scripts/lib/eslint-plugin.mjs +285 -0
- package/scripts/lib/eslint-plugin.node.test.mjs +161 -0
- package/scripts/lib/html-attributes.mjs +191 -0
- package/scripts/lib/mcp-config.mjs +126 -0
- package/scripts/lib/migrate-fix.mjs +115 -0
- package/scripts/lib/validate-bb-markup.mjs +421 -0
- package/dist/ai/BaseButton.md +0 -449
- package/dist/ai/BaseCheckbox.md +0 -512
- package/dist/ai/BaseCheckboxGroup.md +0 -597
- package/dist/ai/BaseColorInput.md +0 -461
- package/dist/ai/BaseDatePicker.md +0 -739
- package/dist/ai/BaseDatePickerInput.md +0 -1517
- package/dist/ai/BaseDialog.md +0 -610
- package/dist/ai/BaseInputContainer.md +0 -570
- package/dist/ai/BaseNumberInput.md +0 -509
- package/dist/ai/BaseRadio.md +0 -405
- package/dist/ai/BaseRadioGroup.md +0 -535
- package/dist/ai/BaseRating.md +0 -489
- package/dist/ai/BaseSelect.md +0 -1742
- package/dist/ai/BaseSlider.md +0 -871
- package/dist/ai/BaseSwitch.md +0 -322
- package/dist/ai/BaseSwitchGroup.md +0 -298
- package/dist/ai/BaseTag.md +0 -623
- package/dist/ai/BaseTextInput.md +0 -392
- package/dist/ai/BaseTextarea.md +0 -398
- package/dist/ai/BbChip.md +0 -147
- package/dist/ai/BbIntersection.md +0 -120
- package/dist/ai/BbRatio.md +0 -61
- package/dist/ai/BbRows.md +0 -307
- package/dist/ai/BbTab.md +0 -950
- package/dist/ai/CommonInputInnerContainer.md +0 -414
- package/dist/ai/CommonInputOuterContainer.md +0 -55
- package/dist/components/BaseButton/BaseButton.vue.d.ts +0 -14
- package/dist/components/BaseButton/types.d.ts +0 -159
- package/dist/components/BaseCheckbox/BaseCheckbox.vue.d.ts +0 -39
- package/dist/components/BaseCheckbox/types.d.ts +0 -213
- package/dist/components/BaseCheckboxGroup/BaseCheckboxGroup.vue.d.ts +0 -30
- package/dist/components/BaseCheckboxGroup/types.d.ts +0 -282
- package/dist/components/BaseColorInput/BaseColorInput.vue.d.ts +0 -35
- package/dist/components/BaseColorInput/ColorPalette.vue.d.ts +0 -13
- package/dist/components/BaseColorInput/types.d.ts +0 -233
- package/dist/components/BaseDatePicker/BaseDatePickerHeader.vue.d.ts +0 -15
- package/dist/components/BaseDatePicker/BaseDatePickerInputDaySelector.vue.d.ts +0 -58
- package/dist/components/BaseDatePicker/BaseDatePickerMonthSelector.vue.d.ts +0 -12
- package/dist/components/BaseDatePicker/BaseDatePickerYearSelector.vue.d.ts +0 -12
- package/dist/components/BaseDatePicker/types.d.ts +0 -100
- package/dist/components/BaseDatePickerInput/BaseDatePickerInput.vue.d.ts +0 -26
- package/dist/components/BaseDatePickerInput/types.d.ts +0 -206
- package/dist/components/BaseDialog/BaseDialog.vue.d.ts +0 -37
- package/dist/components/BaseDialog/types.d.ts +0 -180
- package/dist/components/BaseInputContainer/BaseInputContainer.vue.d.ts +0 -18
- package/dist/components/BaseInputContainer/types.d.ts +0 -126
- package/dist/components/BaseNumberInput/BaseNumberInput.vue.d.ts +0 -46
- package/dist/components/BaseNumberInput/types.d.ts +0 -191
- package/dist/components/BaseRadio/BaseRadio.vue.d.ts +0 -31
- package/dist/components/BaseRadio/types.d.ts +0 -173
- package/dist/components/BaseRadioGroup/BaseRadioGroup.vue.d.ts +0 -30
- package/dist/components/BaseRadioGroup/types.d.ts +0 -240
- package/dist/components/BaseRating/BaseRating.vue.d.ts +0 -35
- package/dist/components/BaseRating/types.d.ts +0 -144
- package/dist/components/BaseSelect/BaseSelect.vue.d.ts +0 -28
- package/dist/components/BaseSelect/types.d.ts +0 -470
- package/dist/components/BaseSlider/BaseSlider.vue.d.ts +0 -38
- package/dist/components/BaseSlider/types.d.ts +0 -201
- package/dist/components/BaseSwitch/BaseSwitch.vue.d.ts +0 -34
- package/dist/components/BaseSwitch/types.d.ts +0 -25
- package/dist/components/BaseSwitchGroup/BaseSwitchGroup.vue.d.ts +0 -30
- package/dist/components/BaseSwitchGroup/types.d.ts +0 -8
- package/dist/components/BaseTag/BaseTag.vue.d.ts +0 -37
- package/dist/components/BaseTag/types.d.ts +0 -136
- package/dist/components/BaseTextInput/BaseTextInput.vue.d.ts +0 -33
- package/dist/components/BaseTextInput/types.d.ts +0 -166
- package/dist/components/BaseTextarea/BaseTextarea.vue.d.ts +0 -31
- package/dist/components/BaseTextarea/types.d.ts +0 -151
- package/dist/components/BbChip/BbChip.vue.d.ts +0 -17
- package/dist/components/BbChip/types.d.ts +0 -23
- package/dist/components/BbIntersection/BbIntersection.vue.d.ts +0 -23
- package/dist/components/BbIntersection/types.d.ts +0 -36
- package/dist/components/BbRatio/BbRatio.vue.d.ts +0 -14
- package/dist/components/BbRatio/types.d.ts +0 -15
- package/dist/components/BbRows/BbRows.vue.d.ts +0 -62
- package/dist/components/BbRows/types.d.ts +0 -21
- package/dist/components/BbTab/BbTab.vue.d.ts +0 -27
- package/dist/components/BbTab/types.d.ts +0 -186
- package/dist/components/CommonInputInnerContainer/CommonInputInnerContainer.vue.d.ts +0 -15
- package/dist/components/CommonInputInnerContainer/types.d.ts +0 -47
- package/dist/components/CommonInputOuterContainer/CommonInputOuterContainer.vue.d.ts +0 -11
- package/dist/components/CommonInputOuterContainer/types.d.ts +0 -16
- package/dist/composables/useModalsState.d.ts +0 -5
- package/dist/composables/useQueue.d.ts +0 -58
- package/dist/composables/useWizard.d.ts +0 -85
- package/dist/i18n/en.d.ts +0 -67
- package/dist/i18n/it.d.ts +0 -67
- package/dist/index.css +0 -2
- package/dist/index100.js +0 -6
- package/dist/index101.js +0 -300
- package/dist/index102.js +0 -16
- package/dist/index103.js +0 -16
- package/dist/index104.js +0 -27
- package/dist/index109.js +0 -19
- package/dist/index11.js +0 -42
- package/dist/index110.js +0 -12
- package/dist/index111.js +0 -19
- package/dist/index112.js +0 -25
- package/dist/index114.js +0 -7
- package/dist/index115.js +0 -241
- package/dist/index116.js +0 -5
- package/dist/index117.js +0 -42
- package/dist/index118.js +0 -5
- package/dist/index119.js +0 -53
- package/dist/index120.js +0 -5
- package/dist/index121.js +0 -32
- package/dist/index122.js +0 -5
- package/dist/index124.js +0 -52
- package/dist/index125.js +0 -5
- package/dist/index126.js +0 -223
- package/dist/index128.js +0 -6
- package/dist/index129.js +0 -162
- package/dist/index13.js +0 -92
- package/dist/index130.js +0 -25
- package/dist/index131.js +0 -51
- package/dist/index132.js +0 -5
- package/dist/index133.js +0 -607
- package/dist/index135.js +0 -6
- package/dist/index136.js +0 -14
- package/dist/index137.js +0 -153
- package/dist/index139.js +0 -6
- package/dist/index14.js +0 -306
- package/dist/index140.js +0 -224
- package/dist/index141.js +0 -5
- package/dist/index143.js +0 -128
- package/dist/index145.js +0 -6
- package/dist/index146.js +0 -8
- package/dist/index147.js +0 -182
- package/dist/index149.js +0 -6
- package/dist/index15.js +0 -10
- package/dist/index151.js +0 -4
- package/dist/index152.js +0 -95
- package/dist/index154.js +0 -6
- package/dist/index155.js +0 -168
- package/dist/index157.js +0 -6
- package/dist/index158.js +0 -145
- package/dist/index16.js +0 -59
- package/dist/index160.js +0 -6
- package/dist/index161.js +0 -53
- package/dist/index163.js +0 -6
- package/dist/index164.js +0 -16
- package/dist/index166.js +0 -35
- package/dist/index168.js +0 -6
- package/dist/index169.js +0 -89
- package/dist/index171.js +0 -6
- package/dist/index172.js +0 -82
- package/dist/index174.js +0 -6
- package/dist/index175.js +0 -38
- package/dist/index176.js +0 -461
- package/dist/index177.js +0 -28
- package/dist/index178.js +0 -66
- package/dist/index18.js +0 -35
- package/dist/index180.js +0 -162
- package/dist/index181.js +0 -5
- package/dist/index182.js +0 -217
- package/dist/index183.js +0 -5
- package/dist/index184.js +0 -112
- package/dist/index185.js +0 -5
- package/dist/index186.js +0 -459
- package/dist/index188.js +0 -6
- package/dist/index19.js +0 -4
- package/dist/index192.js +0 -268
- package/dist/index194.js +0 -6
- package/dist/index195.js +0 -124
- package/dist/index197.js +0 -6
- package/dist/index198.js +0 -174
- package/dist/index2.js +0 -20
- package/dist/index20.js +0 -9
- package/dist/index200.js +0 -6
- package/dist/index201.js +0 -224
- package/dist/index203.js +0 -6
- package/dist/index204.js +0 -135
- package/dist/index206.js +0 -6
- package/dist/index207.js +0 -5
- package/dist/index208.js +0 -170
- package/dist/index21.js +0 -35
- package/dist/index210.js +0 -6
- package/dist/index211.js +0 -37
- package/dist/index213.js +0 -6
- package/dist/index214.js +0 -63
- package/dist/index216.js +0 -6
- package/dist/index217.js +0 -74
- package/dist/index219.js +0 -6
- package/dist/index22.js +0 -6
- package/dist/index220.js +0 -65
- package/dist/index222.js +0 -6
- package/dist/index223.js +0 -67
- package/dist/index225.js +0 -6
- package/dist/index226.js +0 -41
- package/dist/index228.js +0 -6
- package/dist/index229.js +0 -10
- package/dist/index23.js +0 -7
- package/dist/index230.js +0 -22
- package/dist/index232.js +0 -8
- package/dist/index233.js +0 -297
- package/dist/index235.js +0 -6
- package/dist/index236.js +0 -47
- package/dist/index237.js +0 -165
- package/dist/index239.js +0 -6
- package/dist/index24.js +0 -9
- package/dist/index241.js +0 -159
- package/dist/index243.js +0 -6
- package/dist/index244.js +0 -134
- package/dist/index246.js +0 -6
- package/dist/index247.js +0 -167
- package/dist/index249.js +0 -6
- package/dist/index25.js +0 -24
- package/dist/index250.js +0 -147
- package/dist/index252.js +0 -6
- package/dist/index253.js +0 -35
- package/dist/index255.js +0 -6
- package/dist/index256.js +0 -65
- package/dist/index258.js +0 -6
- package/dist/index259.js +0 -19
- package/dist/index260.js +0 -5
- package/dist/index261.js +0 -160
- package/dist/index263.js +0 -6
- package/dist/index264.js +0 -60
- package/dist/index266.js +0 -6
- package/dist/index267.js +0 -134
- package/dist/index269.js +0 -6
- package/dist/index27.js +0 -4
- package/dist/index271.js +0 -206
- package/dist/index273.js +0 -6
- package/dist/index274.js +0 -85
- package/dist/index275.js +0 -5
- package/dist/index276.js +0 -55
- package/dist/index277.js +0 -5
- package/dist/index278.js +0 -164
- package/dist/index28.js +0 -8
- package/dist/index280.js +0 -6
- package/dist/index281.js +0 -145
- package/dist/index283.js +0 -6
- package/dist/index284.js +0 -11
- package/dist/index285.js +0 -200
- package/dist/index287.js +0 -6
- package/dist/index288.js +0 -241
- package/dist/index29.js +0 -7
- package/dist/index290.js +0 -6
- package/dist/index291.js +0 -21
- package/dist/index293.js +0 -6
- package/dist/index294.js +0 -125
- package/dist/index296.js +0 -6
- package/dist/index297.js +0 -161
- package/dist/index299.js +0 -6
- package/dist/index30.js +0 -118
- package/dist/index300.js +0 -127
- package/dist/index302.js +0 -6
- package/dist/index303.js +0 -20
- package/dist/index305.js +0 -6
- package/dist/index306.js +0 -116
- package/dist/index307.js +0 -5
- package/dist/index308.js +0 -507
- package/dist/index31.js +0 -10
- package/dist/index310.js +0 -6
- package/dist/index311.js +0 -196
- package/dist/index313.js +0 -6
- package/dist/index314.js +0 -142
- package/dist/index316.js +0 -6
- package/dist/index317.js +0 -134
- package/dist/index319.js +0 -6
- package/dist/index32.js +0 -28
- package/dist/index320.js +0 -162
- package/dist/index322.js +0 -6
- package/dist/index323.js +0 -319
- package/dist/index325.js +0 -6
- package/dist/index326.js +0 -40
- package/dist/index327.js +0 -43
- package/dist/index328.js +0 -665
- package/dist/index33.js +0 -5
- package/dist/index330.js +0 -6
- package/dist/index331.js +0 -155
- package/dist/index333.js +0 -6
- package/dist/index334.js +0 -142
- package/dist/index336.js +0 -6
- package/dist/index337.js +0 -155
- package/dist/index339.js +0 -6
- package/dist/index34.js +0 -112
- package/dist/index340.js +0 -58
- package/dist/index341.js +0 -5
- package/dist/index342.js +0 -20
- package/dist/index344.js +0 -6
- package/dist/index345.js +0 -142
- package/dist/index347.js +0 -6
- package/dist/index349.js +0 -109
- package/dist/index351.js +0 -6
- package/dist/index353.js +0 -24
- package/dist/index354.js +0 -23
- package/dist/index355.js +0 -839
- package/dist/index356.js +0 -474
- package/dist/index357.js +0 -52
- package/dist/index358.js +0 -7
- package/dist/index36.js +0 -6
- package/dist/index38.js +0 -63
- package/dist/index39.js +0 -63
- package/dist/index40.js +0 -9
- package/dist/index42.js +0 -8
- package/dist/index43.js +0 -179
- package/dist/index44.js +0 -21
- package/dist/index46.js +0 -385
- package/dist/index48.js +0 -6
- package/dist/index49.js +0 -33
- package/dist/index5.js +0 -34
- package/dist/index50.js +0 -12
- package/dist/index51.js +0 -8
- package/dist/index52.js +0 -106
- package/dist/index53.js +0 -330
- package/dist/index54.js +0 -97
- package/dist/index55.js +0 -328
- package/dist/index56.js +0 -28
- package/dist/index57.js +0 -102
- package/dist/index58.js +0 -157
- package/dist/index6.js +0 -38
- package/dist/index60.js +0 -6
- package/dist/index61.js +0 -131
- package/dist/index62.js +0 -5
- package/dist/index65.js +0 -35
- package/dist/index66.js +0 -26
- package/dist/index68.js +0 -6
- package/dist/index69.js +0 -54
- package/dist/index7.js +0 -58
- package/dist/index71.js +0 -6
- package/dist/index72.js +0 -14
- package/dist/index74.js +0 -6
- package/dist/index75.js +0 -159
- package/dist/index77.js +0 -6
- package/dist/index78.js +0 -4
- package/dist/index79.js +0 -4
- package/dist/index80.js +0 -54
- package/dist/index81.js +0 -5
- package/dist/index82.js +0 -5
- package/dist/index83.js +0 -141
- package/dist/index85.js +0 -6
- package/dist/index86.js +0 -65
- package/dist/index88.js +0 -6
- package/dist/index89.js +0 -30
- package/dist/index9.js +0 -11
- package/dist/index90.js +0 -19
- package/dist/index91.js +0 -17
- package/dist/index92.js +0 -9
- package/dist/index93.js +0 -34
- package/dist/index95.js +0 -16
- package/dist/index96.js +0 -10
- package/dist/index97.js +0 -18
- package/dist/index98.js +0 -189
- package/dist/types/VoidHtmlElements.d.ts +0 -1
- package/dist/utilities/functions/add.d.ts +0 -1
- package/dist/utilities/functions/booleanishToBoolean.d.ts +0 -6
- package/dist/utilities/functions/capitalize.d.ts +0 -4
- package/dist/utilities/functions/cloneDeep.d.ts +0 -1
- package/dist/utilities/functions/debounce.d.ts +0 -1
- package/dist/utilities/functions/drop.d.ts +0 -1
- package/dist/utilities/functions/exceedsPrecision.d.ts +0 -1
- package/dist/utilities/functions/findRight.d.ts +0 -4
- package/dist/utilities/functions/findRightIndex.d.ts +0 -4
- package/dist/utilities/functions/flow.d.ts +0 -1
- package/dist/utilities/functions/getHueFromRGB.d.ts +0 -9
- package/dist/utilities/functions/groupBy.d.ts +0 -6
- package/dist/utilities/functions/gt.d.ts +0 -7
- package/dist/utilities/functions/hexToRGB.d.ts +0 -7
- package/dist/utilities/functions/hslToHex.d.ts +0 -1
- package/dist/utilities/functions/includes.d.ts +0 -1
- package/dist/utilities/functions/initial.d.ts +0 -1
- package/dist/utilities/functions/isCssColor.d.ts +0 -5
- package/dist/utilities/functions/lt.d.ts +0 -7
- package/dist/utilities/functions/makeUrl.d.ts +0 -1
- package/dist/utilities/functions/mapKeys.d.ts +0 -5
- package/dist/utilities/functions/mapValues.d.ts +0 -1
- package/dist/utilities/functions/matchesPropertyInsensitive.d.ts +0 -1
- package/dist/utilities/functions/max.d.ts +0 -2
- package/dist/utilities/functions/mean.d.ts +0 -4
- package/dist/utilities/functions/merge.d.ts +0 -11
- package/dist/utilities/functions/millisecondsTo.d.ts +0 -1
- package/dist/utilities/functions/negate.d.ts +0 -4
- package/dist/utilities/functions/normalizeItemGroups.d.ts +0 -13
- package/dist/utilities/functions/omit.d.ts +0 -2
- package/dist/utilities/functions/overEvery.d.ts +0 -4
- package/dist/utilities/functions/pick.d.ts +0 -1
- package/dist/utilities/functions/replace.d.ts +0 -1
- package/dist/utilities/functions/replaceInTree.d.ts +0 -4
- package/dist/utilities/functions/retry.d.ts +0 -5
- package/dist/utilities/functions/roundToTheNearest.d.ts +0 -8
- package/dist/utilities/functions/set.d.ts +0 -15
- package/dist/utilities/functions/stub.d.ts +0 -6
- package/dist/utilities/functions/transposePercent.d.ts +0 -9
- package/dist/utilities/functions/tryTo.d.ts +0 -2
- package/dist/utilities/functions/uniqBy.d.ts +0 -1
- /package/dist/{index64.js → _virtual/_plugin-vue_export-helper.js} +0 -0
- /package/dist/components/{BaseButton → BbBaseButton}/RouterComponent.vue.d.ts +0 -0
- /package/dist/components/{BaseDatePicker/BaseDatePicker.vue.d.ts → BbBaseDatePicker/BbBaseDatePicker.vue.d.ts} +0 -0
- /package/dist/{index165.js → composables/useArray.js} +0 -0
- /package/dist/{index17.js → composables/useFrameworkDetection.js} +0 -0
- /package/dist/{index12.js → composables/useId.js} +0 -0
- /package/dist/{index352.js → directives/createPopoverDirective.js} +0 -0
- /package/dist/{index107.js → utilities/functions/chunk.js} +0 -0
- /package/dist/{index8.js → utilities/functions/clamp.js} +0 -0
- /package/dist/{index150.js → utilities/functions/curry.js} +0 -0
- /package/dist/{index45.js → utilities/functions/extractDomContainer.js} +0 -0
- /package/dist/{index270.js → utilities/functions/findInTree.js} +0 -0
- /package/dist/{index348.js → utilities/functions/flattenTree.js} +0 -0
- /package/dist/{index41.js → utilities/functions/get.js} +0 -0
- /package/dist/{index189.js → utilities/functions/head.js} +0 -0
- /package/dist/{index108.js → utilities/functions/identity.js} +0 -0
- /package/dist/{index179.js → utilities/functions/indexBy.js} +0 -0
- /package/dist/{index37.js → utilities/functions/isHex.js} +0 -0
- /package/dist/{index94.js → utilities/functions/isNil.js} +0 -0
- /package/dist/{index26.js → utilities/functions/isNotNil.js} +0 -0
- /package/dist/{index105.js → utilities/functions/last.js} +0 -0
- /package/dist/{index142.js → utilities/functions/noop.js} +0 -0
- /package/dist/{index231.js → utilities/functions/parseWidthString.js} +0 -0
- /package/dist/{index240.js → utilities/functions/pickBy.js} +0 -0
- /package/dist/{index123.js → utilities/functions/range.js} +0 -0
- /package/dist/{index190.js → utilities/functions/snapToStep.js} +0 -0
- /package/dist/{index191.js → utilities/functions/transpose.js} +0 -0
- /package/dist/{index106.js → utilities/functions/validateDateFormat.js} +0 -0
- /package/dist/{index10.js → utilities/functions/wait.js} +0 -0
|
@@ -0,0 +1,1598 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Approvals Inbox (Inertia)
|
|
3
|
+
summary: Keyboard-first expense-approvals triage for Laravel + Inertia — `keyboard-navigation` with highlight-opens-peek, one-click approve, reject-with-reason and snooze dialogs via promise-wrapped visits, bulk approve/reject with select-all-across-pages, and an `A`/`R`/`S` shortcut bar, all through one page context that coopts the Inertia Helpers toolkit.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Recipe: Approvals Inbox (Inertia)
|
|
7
|
+
|
|
8
|
+
The densest advanced-page recipe in the folder, adapted for **Laravel +
|
|
9
|
+
Inertia**: the same Linear-triage × Expensify-approvals expense inbox —
|
|
10
|
+
`keyboard-navigation` with the highlight driving the peek, one-click
|
|
11
|
+
approve, reject-with-reason and snooze dialogs, a floating bulk-action bar
|
|
12
|
+
with select-all-across-pages, and an `A`/`R`/`S` shortcut bar — but the list
|
|
13
|
+
is a page prop via [`useTableContext`](../../guides/inertia-helpers.md),
|
|
14
|
+
filters mirror into the URL through
|
|
15
|
+
[`useQueryObject`](../../guides/inertia-helpers.md), tabs are real,
|
|
16
|
+
partial-reloaded server visits, and every mutation is a promise-wrapped
|
|
17
|
+
`router.visit`. Anatomy, components, the context shape, copy and data are
|
|
18
|
+
identical to the Vue recipe; only the data/URL/mutation layer changes.
|
|
19
|
+
|
|
20
|
+
Sibling recipes: [Vue](../vue/approvals-inbox.md) ·
|
|
21
|
+
[Nuxt](../nuxt/approvals-inbox.md).
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
- `<BbConfirm />` and `<BbToast />` mounted once in the app layout.
|
|
26
|
+
- From the [Inertia Helpers](../../guides/inertia-helpers.md) toolkit:
|
|
27
|
+
`useTableContext`, `useQueryObject`, `useResourceContext`, `useProp`,
|
|
28
|
+
`useQuery`.
|
|
29
|
+
- Controller: `Inertia::render('Approvals/Index', ['expenses' =>
|
|
30
|
+
ExpensePaginator::make(...), 'tabCounts' => [...]])` — `expenses` is a
|
|
31
|
+
standard Laravel paginator (`PaginatedResponse<Expense>`) and `tabCounts`
|
|
32
|
+
a plain `{ needsApproval, approved, rejected, all }` map. Both are re-sent
|
|
33
|
+
on every partial reload the page triggers
|
|
34
|
+
(`only: ['expenses', 'tabCounts']`). Mutation routes are plain Laravel
|
|
35
|
+
routes (`PATCH /expenses/:id/approve`, `POST /expenses/:id/reject`,
|
|
36
|
+
`PATCH /expenses/:id/snooze`, `PATCH /expenses/bulk/approve`,
|
|
37
|
+
`POST /expenses/bulk/reject`) that redirect back; small JSON GET
|
|
38
|
+
endpoints (`GET /expenses/:id/peek`, `GET /submitters/:id/peek`,
|
|
39
|
+
`GET /submitters/search`) back the sidebar peeks and the submitter filter
|
|
40
|
+
pill so opening one is not a full-page visit.
|
|
41
|
+
|
|
42
|
+
## Key ideas
|
|
43
|
+
|
|
44
|
+
- **Highlight drives the peek — but it is a plain local ref here.** The
|
|
45
|
+
Inertia data context (`useTableContext`) mirrors only what the server
|
|
46
|
+
sent (items, pagination, selection); it has no concept of "the row under
|
|
47
|
+
the roving tabindex" because the server never sends that. So
|
|
48
|
+
`ctx.highlighted` is a small local ref bound to `BbTable`'s own
|
|
49
|
+
`v-model:highlighted`, watched in `context.ts` exactly like the Vue/Nuxt
|
|
50
|
+
`table.highlighted` — every highlight change still replaces the
|
|
51
|
+
sidebar's stack with that expense, and `keyboard-navigation`'s
|
|
52
|
+
Enter/Space still shares the mouse's code path for free.
|
|
53
|
+
- **One coopting page context.** `provideApprovalsInbox()` joins the
|
|
54
|
+
page-shell stack **and** the keyed `useTableContext('expenses')` once,
|
|
55
|
+
owns filters/tab/dialogs/highlight, and exposes complete actions. Every
|
|
56
|
+
leaf calls `useApprovalsInbox()` and nothing else.
|
|
57
|
+
- **The table has no client-side fetch at all.** `expenses` arrives as a
|
|
58
|
+
page prop; `useTableContext('expenses')` turns it into `items` +
|
|
59
|
+
`totalItems`/`page`/`perPage` (URL-backed) + `selected`/`unselected`/`all`
|
|
60
|
+
(select-all-across-pages), and `BbTable` binds directly to that handle —
|
|
61
|
+
no `dependencies`, no items function.
|
|
62
|
+
- **Filters mirror into the URL with `useQueryObject`.** Typing in a pill
|
|
63
|
+
filter performs a debounced partial Inertia visit
|
|
64
|
+
(`only: ['expenses', 'tabCounts']`); the server is the single source of
|
|
65
|
+
filtered, paginated truth.
|
|
66
|
+
- **Tabs are real visits.** `BbTabs` items carry `href` + `server: true`
|
|
67
|
+
(component-level `navigation` is required alongside it) so switching tabs
|
|
68
|
+
is a genuine, partial-reloaded Inertia visit — not a client-only URL
|
|
69
|
+
change.
|
|
70
|
+
- **Mutations are promise-wrapped `router.visit` calls.** A tiny local
|
|
71
|
+
`visit()` helper turns Inertia's callback-based API into a promise, so
|
|
72
|
+
context actions stay `async function`s exactly like the Vue/Nuxt
|
|
73
|
+
versions — `await ctx.approve(...)` reads the same everywhere.
|
|
74
|
+
- **Three dispatch surfaces, one set of actions.** Approve / reject /
|
|
75
|
+
snooze are reachable from the row, from inside the peek, and from the
|
|
76
|
+
`A`/`R`/`S` keyboard shortcuts — all three call the exact same context
|
|
77
|
+
functions (`approve`, `openReject`, `openSnooze`), so there is exactly
|
|
78
|
+
one place each action's visit, refresh and toast live.
|
|
79
|
+
- **Reason and date are dialogs, not confirms.** Reject needs a typed
|
|
80
|
+
reason and snooze needs a date, so both open a small `BbDialog` (the
|
|
81
|
+
dialog itself is the confirmation gate) — a plain `useConfirm` is
|
|
82
|
+
reserved for actions that need no input.
|
|
83
|
+
- **Bulk actions carry the full select-all-across-pages payload**
|
|
84
|
+
(`ids`/`all`/`unselected`) so "select all, deselect three, approve" sends
|
|
85
|
+
the same semantics the table itself models.
|
|
86
|
+
- **The shortcut bar mirrors `useDeleteCmd`'s guard.** A small keydown
|
|
87
|
+
handler in the context checks `isInteractiveElementFocused()` before
|
|
88
|
+
firing — typing "reason" in the reject textarea never triggers `R`. This
|
|
89
|
+
code is byte-for-byte identical across all three platforms.
|
|
90
|
+
- **Clicking a submitter's name always stacks**, fetched from a small JSON
|
|
91
|
+
endpoint via `useResourceContext` — not a full-page visit, so opening the
|
|
92
|
+
sidebar never replaces the list underneath it.
|
|
93
|
+
- **Buttons are `md` by default**; `sm`/`xs` apply only where the doctrine
|
|
94
|
+
allows it — table rows, the peek footers, the bulk bar, the shortcut bar.
|
|
95
|
+
|
|
96
|
+
## Page anatomy
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
ApprovalsInbox/
|
|
100
|
+
Index.vue ← assembly: PageShell slots, table, tabs, pagination
|
|
101
|
+
context.ts ← coopting page context (shell + useTableContext + filters + highlight + shortcuts + actions)
|
|
102
|
+
FilterRail.vue ← pill filters + clear-all
|
|
103
|
+
ShortcutsHint.vue ← A / R / S icon buttons, tooltip-labelled, act on the highlighted row
|
|
104
|
+
ReceiptThumbnail.vue ← receipt image/placeholder cell
|
|
105
|
+
RowActions.vue ← per-row approve / reject / snooze icon buttons
|
|
106
|
+
SelectionBar.vue ← bulk approve / reject in the floating bottom panel
|
|
107
|
+
RejectDialog.vue ← single-row reject, required reason
|
|
108
|
+
BulkRejectDialog.vue ← bulk reject, required reason
|
|
109
|
+
SnoozeDialog.vue ← single-row snooze, date picker
|
|
110
|
+
ExpensePeek.vue ← sidebar workbench, stack level 1 (opens from highlight)
|
|
111
|
+
SubmitterPeek.vue ← stacked peek, back arrow via the shell
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Chrome assumed from the guides: [App Layout](../../guides/app-layout.md)
|
|
115
|
+
(left nav + fat footer), [Page Shell](../../guides/page-shell.md),
|
|
116
|
+
[Floating Bottom Panel](../../guides/floating-bottom-panel.md).
|
|
117
|
+
|
|
118
|
+
## Action inventory
|
|
119
|
+
|
|
120
|
+
| # | Action | Trigger | Mechanism |
|
|
121
|
+
| --- | -------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------- |
|
|
122
|
+
| 1 | Page search (merchant/submitter) | topbar right | `filters.search` → `useQueryObject` partial visit |
|
|
123
|
+
| 2 | Filter by submitter | filter rail pill | `BbSelectPopover` single, query-aware (small JSON endpoint) |
|
|
124
|
+
| 3 | Filter by category | filter rail pill | `BbSelectPopover` multiple |
|
|
125
|
+
| 4 | Quick filter: amount over | filter rail pill | `BbSelectPopover` single, preset thresholds |
|
|
126
|
+
| 5 | Clear filters | rail right (only when active) | context `clearFilters()` |
|
|
127
|
+
| 6 | Switch tab | server-visit tabs with counts | `BbTabs` `href` + `server: true` visit |
|
|
128
|
+
| 7 | Arrow through rows | `keyboard-navigation` roving tabindex | table's own Arrow/Home/End |
|
|
129
|
+
| 8 | Preview an expense | highlight a row (click, Enter/Space, arrow-then-Enter) | `ctx.highlighted` watcher → `peekExpense` (replace), JSON fetch |
|
|
130
|
+
| 9 | Approve (one click) | row icon button, peek footer, key `A` | `approve(expense)` — promise-wrapped visit + toast |
|
|
131
|
+
| 10 | Reject with reason | row icon button, peek footer, key `R` | `openReject` → `RejectDialog` (required `BbTextarea`) |
|
|
132
|
+
| 11 | Snooze | row icon button, peek footer, key `S` | `openSnooze` → `SnoozeDialog` (`BbDatePickerInput`) |
|
|
133
|
+
| 12 | Select rows / select all | table checkboxes | `useTableContext` selection (across pages) |
|
|
134
|
+
| 13 | Bulk approve | floating bottom panel | `bulkApprove()` on `table.selected`/`all`/`unselected` |
|
|
135
|
+
| 14 | Bulk reject with reason | floating bottom panel | `BulkRejectDialog`, destructive |
|
|
136
|
+
| 15 | Stack a submitter | submitter cell / peek property | `stackSubmitter(id)` → shell stack, JSON fetch |
|
|
137
|
+
| 16 | Stack an expense from a profile | recent-expense row inside `SubmitterPeek` | `stackExpense(id)` → shell stack, JSON fetch |
|
|
138
|
+
| 17 | View full-size receipt | peek "View receipt" link | navigation (`href`, new tab) |
|
|
139
|
+
| 18 | Open full expense | peek footer | navigation (`href="/expenses/:id"`) |
|
|
140
|
+
| 19 | Back / close peek | shell chrome | shell stack pop / close |
|
|
141
|
+
|
|
142
|
+
Nineteen distinct actions, spanning inline one-click mutation (9),
|
|
143
|
+
input-gated mutation via dialog (10, 11, 14), bulk mutation (13, 14),
|
|
144
|
+
keyboard dispatch (7, 9, 10, 11), navigation (17, 18, 19) and contextual
|
|
145
|
+
drill-down (15, 16) — well past the ≥ 12 bar.
|
|
146
|
+
|
|
147
|
+
## The code
|
|
148
|
+
|
|
149
|
+
### context.ts
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// ApprovalsInbox/context.ts
|
|
153
|
+
import {
|
|
154
|
+
computed,
|
|
155
|
+
inject,
|
|
156
|
+
provide,
|
|
157
|
+
reactive,
|
|
158
|
+
ref,
|
|
159
|
+
watch,
|
|
160
|
+
type ComputedRef,
|
|
161
|
+
type InjectionKey,
|
|
162
|
+
type Ref,
|
|
163
|
+
} from 'vue';
|
|
164
|
+
import { router } from '@inertiajs/vue3';
|
|
165
|
+
import type { RequestPayload } from '@inertiajs/core';
|
|
166
|
+
import { useMagicKeys, whenever } from '@vueuse/core';
|
|
167
|
+
import { useConfirm, useToast } from 'bitboss-ui';
|
|
168
|
+
import {
|
|
169
|
+
usePageShellContext,
|
|
170
|
+
type PageShellContext,
|
|
171
|
+
} from '@/components/layout/usePageShellContext';
|
|
172
|
+
import {
|
|
173
|
+
useTableContext,
|
|
174
|
+
type TableContext,
|
|
175
|
+
} from '@/composables/useTableContext';
|
|
176
|
+
import { useQueryObject } from '@/composables/useQueryObject';
|
|
177
|
+
import { useProp } from '@/composables/useProp';
|
|
178
|
+
import { useQuery } from '@/composables/useQuery';
|
|
179
|
+
import type { Expense, ExpenseCategory } from '@/types';
|
|
180
|
+
|
|
181
|
+
/** What the stacked sidebar can show. Clicking a name pushes one of these. */
|
|
182
|
+
export type PeekItem =
|
|
183
|
+
| { type: 'expense'; id: number }
|
|
184
|
+
| { type: 'submitter'; id: number };
|
|
185
|
+
|
|
186
|
+
export type ApprovalsTab = 'needsApproval' | 'approved' | 'rejected' | 'all';
|
|
187
|
+
|
|
188
|
+
export interface ApprovalsFilters {
|
|
189
|
+
search: string;
|
|
190
|
+
submitterId: number | null;
|
|
191
|
+
categories: ExpenseCategory[];
|
|
192
|
+
/** Preset threshold from the quick filter (`50`, `100`, `250`…), or `null`. */
|
|
193
|
+
amountOver: number | null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const RELOAD_PROPS = ['expenses', 'tabCounts'] as const;
|
|
197
|
+
|
|
198
|
+
export interface ApprovalsInboxContext {
|
|
199
|
+
// -- Coopted contexts ------------------------------------------------------
|
|
200
|
+
shell: PageShellContext<PeekItem>;
|
|
201
|
+
table: TableContext<Expense, number>;
|
|
202
|
+
/**
|
|
203
|
+
* The row under the roving tabindex. A plain local ref — the data context
|
|
204
|
+
* above mirrors only what the server sent, and highlight is not server
|
|
205
|
+
* data — bound to `BbTable`'s own `v-model:highlighted`.
|
|
206
|
+
*/
|
|
207
|
+
highlighted: Ref<number | null>;
|
|
208
|
+
// -- Page-owned state --------------------------------------------------------
|
|
209
|
+
filters: ApprovalsFilters;
|
|
210
|
+
hasActiveFilters: ComputedRef<boolean>;
|
|
211
|
+
tab: Ref<ApprovalsTab>;
|
|
212
|
+
tabCounts: ComputedRef<Record<ApprovalsTab, number>>;
|
|
213
|
+
dialogs: {
|
|
214
|
+
reject: Ref<Expense | null>;
|
|
215
|
+
bulkReject: Ref<boolean>;
|
|
216
|
+
snooze: Ref<Expense | null>;
|
|
217
|
+
};
|
|
218
|
+
/** Bumped after every mutation — peeks watch it to re-fetch their JSON detail. */
|
|
219
|
+
refreshKey: Ref<number>;
|
|
220
|
+
/** The expense under the roving tabindex, resolved from the current page's items. */
|
|
221
|
+
highlightedExpense: ComputedRef<Expense | null>;
|
|
222
|
+
// -- Peek grammar ----------------------------------------------------------
|
|
223
|
+
// (peekExpense — the highlight-driven replace — is internal to the watcher
|
|
224
|
+
// below, not part of the public surface: leaves never open the peek
|
|
225
|
+
// directly, only highlight drives it.)
|
|
226
|
+
stackExpense: (id: number) => void;
|
|
227
|
+
stackSubmitter: (id: number) => void;
|
|
228
|
+
// -- Actions (complete: visit + refresh + toast) -----------------------------
|
|
229
|
+
clearFilters: () => void;
|
|
230
|
+
approve: (expense: Expense) => Promise<void>;
|
|
231
|
+
openReject: (expense: Expense) => void;
|
|
232
|
+
reject: (expense: Expense, reason: string) => Promise<void>;
|
|
233
|
+
openSnooze: (expense: Expense) => void;
|
|
234
|
+
snooze: (expense: Expense, until: string) => Promise<void>;
|
|
235
|
+
bulkApprove: () => Promise<void>;
|
|
236
|
+
openBulkReject: () => void;
|
|
237
|
+
bulkReject: (reason: string) => Promise<void>;
|
|
238
|
+
// -- Keyboard-shortcut dispatch (mirrors the row/peek buttons) --------------
|
|
239
|
+
approveHighlighted: () => void;
|
|
240
|
+
rejectHighlighted: () => void;
|
|
241
|
+
snoozeHighlighted: () => void;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const key: InjectionKey<ApprovalsInboxContext> = Symbol('approvals-inbox');
|
|
245
|
+
|
|
246
|
+
/** Mirrors `useDeleteCmd`'s guard: skip shortcuts while the user is typing. */
|
|
247
|
+
const isInteractiveElementFocused = (): boolean => {
|
|
248
|
+
const active = document.activeElement;
|
|
249
|
+
if (!active) return false;
|
|
250
|
+
const tag = active.tagName.toLowerCase();
|
|
251
|
+
if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;
|
|
252
|
+
return active.getAttribute('contenteditable') === 'true';
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/** Comma-joined query value → typed array (or `[]` when absent). */
|
|
256
|
+
const toArray = <T = string>(value: unknown): T[] => {
|
|
257
|
+
if (value == null || value === '') return [];
|
|
258
|
+
const raw = Array.isArray(value) ? value : String(value).split(',');
|
|
259
|
+
return raw as T[];
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/** Inertia's callback-based visit, wrapped as a promise — so every context
|
|
263
|
+
* action stays `async`, exactly like the Vue/Nuxt versions. Always a
|
|
264
|
+
* partial reload of the props this page depends on. */
|
|
265
|
+
const visit = (
|
|
266
|
+
method: 'post' | 'put' | 'patch' | 'delete',
|
|
267
|
+
url: string,
|
|
268
|
+
// Inertia's own payload type — strings/numbers/booleans/arrays/files.
|
|
269
|
+
// (`Record<string, unknown>` does not satisfy it.)
|
|
270
|
+
data: RequestPayload = {}
|
|
271
|
+
): Promise<void> =>
|
|
272
|
+
new Promise((resolve, reject) => {
|
|
273
|
+
router.visit(url, {
|
|
274
|
+
method,
|
|
275
|
+
data,
|
|
276
|
+
only: [...RELOAD_PROPS],
|
|
277
|
+
preserveScroll: true,
|
|
278
|
+
preserveState: true,
|
|
279
|
+
onSuccess: () => resolve(),
|
|
280
|
+
onError: (errors) => reject(errors),
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
export function provideApprovalsInbox(): ApprovalsInboxContext {
|
|
285
|
+
const { toast } = useToast();
|
|
286
|
+
const { confirm } = useConfirm();
|
|
287
|
+
|
|
288
|
+
// -- Coopt the specific contexts: once, here, for the whole page. ----------
|
|
289
|
+
const shell = usePageShellContext<PeekItem>();
|
|
290
|
+
// `Expense, number`: BbTable's `item-value="id"` resolves selection to
|
|
291
|
+
// expense ids, matching the Vue/Nuxt `table.selected: number[]` shape.
|
|
292
|
+
const table = useTableContext<Expense, number>('expenses');
|
|
293
|
+
// Highlight is not server data, so it does not live on `table` — a plain
|
|
294
|
+
// local ref bound to BbTable's own `v-model:highlighted`.
|
|
295
|
+
const highlighted = ref<number | null>(null);
|
|
296
|
+
|
|
297
|
+
// -- Filters: seeded from the current URL, mirrored back by useQueryObject. -
|
|
298
|
+
const query = useQuery();
|
|
299
|
+
const filters = reactive<ApprovalsFilters>({
|
|
300
|
+
search: (query.search as string) ?? '',
|
|
301
|
+
submitterId: query.submitterId ? Number(query.submitterId) : null,
|
|
302
|
+
categories: toArray<ExpenseCategory>(query.categories),
|
|
303
|
+
amountOver: query.amountOver ? Number(query.amountOver) : null,
|
|
304
|
+
});
|
|
305
|
+
useQueryObject(filters, { only: [...RELOAD_PROPS] });
|
|
306
|
+
|
|
307
|
+
const hasActiveFilters = computed(
|
|
308
|
+
() =>
|
|
309
|
+
filters.search !== '' ||
|
|
310
|
+
filters.submitterId !== null ||
|
|
311
|
+
filters.categories.length > 0 ||
|
|
312
|
+
filters.amountOver !== null
|
|
313
|
+
);
|
|
314
|
+
const clearFilters = () => {
|
|
315
|
+
filters.search = '';
|
|
316
|
+
filters.submitterId = null;
|
|
317
|
+
filters.categories = [];
|
|
318
|
+
filters.amountOver = null;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Tab: seeded from the URL; BbTabs' own `href` + `server` visit updates
|
|
322
|
+
// the URL, and its `v-model` write keeps this ref in sync with the click.
|
|
323
|
+
const tab = ref<ApprovalsTab>((query.tab as ApprovalsTab) ?? 'needsApproval');
|
|
324
|
+
const tabCounts = useProp<Record<ApprovalsTab, number>>('tabCounts');
|
|
325
|
+
|
|
326
|
+
const refreshKey = ref(0);
|
|
327
|
+
const dialogs = {
|
|
328
|
+
reject: ref<Expense | null>(null),
|
|
329
|
+
bulkReject: ref(false),
|
|
330
|
+
snooze: ref<Expense | null>(null),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// -- Peek grammar -----------------------------------------------------------------
|
|
334
|
+
// peekX replaces the stack (from the list / highlight); stackX drills down.
|
|
335
|
+
const peekExpense = (id: number) => shell.push({ type: 'expense', id }, true);
|
|
336
|
+
const stackExpense = (id: number) => shell.push({ type: 'expense', id });
|
|
337
|
+
const stackSubmitter = (id: number) => shell.push({ type: 'submitter', id });
|
|
338
|
+
|
|
339
|
+
// Highlighting IS the peek trigger: arrowing (or clicking) a row opens it,
|
|
340
|
+
// clearing the highlight closes it.
|
|
341
|
+
watch(highlighted, (id) => {
|
|
342
|
+
if (id == null) {
|
|
343
|
+
shell.close();
|
|
344
|
+
} else {
|
|
345
|
+
peekExpense(id);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const highlightedExpense = computed<Expense | null>(
|
|
350
|
+
() =>
|
|
351
|
+
table.items.value.find((expense) => expense.id === highlighted.value) ??
|
|
352
|
+
null
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// -- Actions ----------------------------------------------------------------------
|
|
356
|
+
const refresh = () => {
|
|
357
|
+
refreshKey.value++;
|
|
358
|
+
};
|
|
359
|
+
const clearSelection = () => {
|
|
360
|
+
table.resetSelection();
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const approve = async (expense: Expense) => {
|
|
364
|
+
await visit('patch', `/expenses/${expense.id}/approve`);
|
|
365
|
+
refresh();
|
|
366
|
+
toast({
|
|
367
|
+
title: `Approved ${expense.merchant} — $${expense.amount.toFixed(2)}.`,
|
|
368
|
+
variant: 'success',
|
|
369
|
+
});
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const openReject = (expense: Expense) => {
|
|
373
|
+
dialogs.reject.value = expense;
|
|
374
|
+
};
|
|
375
|
+
const reject = async (expense: Expense, reason: string) => {
|
|
376
|
+
await visit('post', `/expenses/${expense.id}/reject`, { reason });
|
|
377
|
+
dialogs.reject.value = null;
|
|
378
|
+
refresh();
|
|
379
|
+
toast({ title: `Rejected ${expense.merchant}.`, variant: 'success' });
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const openSnooze = (expense: Expense) => {
|
|
383
|
+
dialogs.snooze.value = expense;
|
|
384
|
+
};
|
|
385
|
+
const snooze = async (expense: Expense, until: string) => {
|
|
386
|
+
await visit('patch', `/expenses/${expense.id}/snooze`, { until });
|
|
387
|
+
dialogs.snooze.value = null;
|
|
388
|
+
refresh();
|
|
389
|
+
toast({ title: `${expense.merchant} snoozed.`, variant: 'success' });
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// Bulk actions carry the full select-all-across-pages payload — the
|
|
393
|
+
// server combines `all` + `unselected` the same way BbTable does.
|
|
394
|
+
const bulkPayload = () => ({
|
|
395
|
+
ids: table.selected.value,
|
|
396
|
+
all: table.all.value,
|
|
397
|
+
unselected: table.unselected.value,
|
|
398
|
+
});
|
|
399
|
+
const bulkCount = () =>
|
|
400
|
+
table.all.value
|
|
401
|
+
? table.totalItems.value - table.unselected.value.length
|
|
402
|
+
: table.selected.value.length;
|
|
403
|
+
|
|
404
|
+
const bulkApprove = async () => {
|
|
405
|
+
const count = bulkCount();
|
|
406
|
+
await visit('patch', '/expenses/bulk/approve', bulkPayload());
|
|
407
|
+
clearSelection();
|
|
408
|
+
refresh();
|
|
409
|
+
toast({ title: `${count} expenses approved.`, variant: 'success' });
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const openBulkReject = () => {
|
|
413
|
+
dialogs.bulkReject.value = true;
|
|
414
|
+
};
|
|
415
|
+
const bulkReject = async (reason: string) => {
|
|
416
|
+
const count = bulkCount();
|
|
417
|
+
const ok = await confirm({
|
|
418
|
+
variant: 'destructive',
|
|
419
|
+
title: 'Reject expenses',
|
|
420
|
+
text: `Reject ${count} expenses? The submitters are notified.`,
|
|
421
|
+
yes: 'Reject',
|
|
422
|
+
no: 'Cancel',
|
|
423
|
+
});
|
|
424
|
+
if (!ok) return;
|
|
425
|
+
await visit('post', '/expenses/bulk/reject', { ...bulkPayload(), reason });
|
|
426
|
+
dialogs.bulkReject.value = false;
|
|
427
|
+
clearSelection();
|
|
428
|
+
refresh();
|
|
429
|
+
toast({ title: `${count} expenses rejected.`, variant: 'success' });
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// -- Keyboard-shortcut dispatch: same actions, resolved against the row
|
|
433
|
+
// under the roving tabindex. Guarded exactly like `useDeleteCmd`.
|
|
434
|
+
const keys = useMagicKeys();
|
|
435
|
+
const withHighlighted = (run: (expense: Expense) => void) => {
|
|
436
|
+
if (isInteractiveElementFocused()) return;
|
|
437
|
+
if (
|
|
438
|
+
dialogs.reject.value ||
|
|
439
|
+
dialogs.bulkReject.value ||
|
|
440
|
+
dialogs.snooze.value
|
|
441
|
+
)
|
|
442
|
+
return;
|
|
443
|
+
const expense = highlightedExpense.value;
|
|
444
|
+
if (expense) run(expense);
|
|
445
|
+
};
|
|
446
|
+
const approveHighlighted = () =>
|
|
447
|
+
withHighlighted((expense) => void approve(expense));
|
|
448
|
+
const rejectHighlighted = () => withHighlighted(openReject);
|
|
449
|
+
const snoozeHighlighted = () => withHighlighted(openSnooze);
|
|
450
|
+
|
|
451
|
+
whenever(keys['a'], approveHighlighted);
|
|
452
|
+
whenever(keys['r'], rejectHighlighted);
|
|
453
|
+
whenever(keys['s'], snoozeHighlighted);
|
|
454
|
+
|
|
455
|
+
const ctx: ApprovalsInboxContext = {
|
|
456
|
+
shell,
|
|
457
|
+
table,
|
|
458
|
+
highlighted,
|
|
459
|
+
filters,
|
|
460
|
+
hasActiveFilters,
|
|
461
|
+
tab,
|
|
462
|
+
tabCounts,
|
|
463
|
+
dialogs,
|
|
464
|
+
refreshKey,
|
|
465
|
+
highlightedExpense,
|
|
466
|
+
stackExpense,
|
|
467
|
+
stackSubmitter,
|
|
468
|
+
clearFilters,
|
|
469
|
+
approve,
|
|
470
|
+
openReject,
|
|
471
|
+
reject,
|
|
472
|
+
openSnooze,
|
|
473
|
+
snooze,
|
|
474
|
+
bulkApprove,
|
|
475
|
+
openBulkReject,
|
|
476
|
+
bulkReject,
|
|
477
|
+
approveHighlighted,
|
|
478
|
+
rejectHighlighted,
|
|
479
|
+
snoozeHighlighted,
|
|
480
|
+
};
|
|
481
|
+
provide(key, ctx);
|
|
482
|
+
return ctx;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export function useApprovalsInbox(): ApprovalsInboxContext {
|
|
486
|
+
const ctx = inject(key);
|
|
487
|
+
if (!ctx)
|
|
488
|
+
throw new Error('useApprovalsInbox() called outside ApprovalsInbox');
|
|
489
|
+
return ctx;
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Index.vue — the assembly
|
|
494
|
+
|
|
495
|
+
```vue
|
|
496
|
+
<!-- ApprovalsInbox/Index.vue -->
|
|
497
|
+
<template>
|
|
498
|
+
<PageShell>
|
|
499
|
+
<!-- Topbar: breadcrumbs left, page search right. -->
|
|
500
|
+
<template #topbar>
|
|
501
|
+
<div class="flex items-center justify-between gap-4 border-b px-4 py-2">
|
|
502
|
+
<!-- Breadcrumbs take the row's slack on a stable basis (min-w-0 flex-auto);
|
|
503
|
+
without it their overflow recompute fights the flex layout every frame. -->
|
|
504
|
+
<BbBreadcrumbs
|
|
505
|
+
class="min-w-0 flex-auto"
|
|
506
|
+
:items="[
|
|
507
|
+
{ key: 'dashboard', text: 'Dashboard', href: '/dashboard' },
|
|
508
|
+
{ key: 'finance', text: 'Finance', href: '/finance' },
|
|
509
|
+
{ key: 'approvals', text: 'Approvals' },
|
|
510
|
+
]"
|
|
511
|
+
/>
|
|
512
|
+
<BbTextInput
|
|
513
|
+
v-model="ctx.filters.search"
|
|
514
|
+
class="shrink-0"
|
|
515
|
+
clearable
|
|
516
|
+
compact
|
|
517
|
+
hide-label
|
|
518
|
+
label="Search expenses"
|
|
519
|
+
placeholder="Search merchant or submitter…"
|
|
520
|
+
prepend:icon="lucide:search"
|
|
521
|
+
/>
|
|
522
|
+
</div>
|
|
523
|
+
</template>
|
|
524
|
+
|
|
525
|
+
<div class="flex flex-col gap-3 p-4">
|
|
526
|
+
<FilterRail />
|
|
527
|
+
|
|
528
|
+
<div class="flex items-center justify-between gap-2">
|
|
529
|
+
<!-- Tabs are real visits: href + server (component-level `navigation`
|
|
530
|
+
is required alongside per-item `server`), each carrying the same
|
|
531
|
+
partial-reload prop list as the filter watcher. -->
|
|
532
|
+
<BbTabs
|
|
533
|
+
v-model="ctx.tab.value"
|
|
534
|
+
compact
|
|
535
|
+
navigation
|
|
536
|
+
preserve-scroll
|
|
537
|
+
preserve-state
|
|
538
|
+
replace
|
|
539
|
+
:items="[
|
|
540
|
+
{
|
|
541
|
+
key: 'needsApproval',
|
|
542
|
+
label: `Needs approval · ${ctx.tabCounts.value.needsApproval}`,
|
|
543
|
+
href: '/approvals?tab=needsApproval',
|
|
544
|
+
server: true,
|
|
545
|
+
only: ['expenses', 'tabCounts'],
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
key: 'approved',
|
|
549
|
+
label: `Approved · ${ctx.tabCounts.value.approved}`,
|
|
550
|
+
href: '/approvals?tab=approved',
|
|
551
|
+
server: true,
|
|
552
|
+
only: ['expenses', 'tabCounts'],
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
key: 'rejected',
|
|
556
|
+
label: `Rejected · ${ctx.tabCounts.value.rejected}`,
|
|
557
|
+
href: '/approvals?tab=rejected',
|
|
558
|
+
server: true,
|
|
559
|
+
only: ['expenses', 'tabCounts'],
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
key: 'all',
|
|
563
|
+
label: `All · ${ctx.tabCounts.value.all}`,
|
|
564
|
+
href: '/approvals?tab=all',
|
|
565
|
+
server: true,
|
|
566
|
+
only: ['expenses', 'tabCounts'],
|
|
567
|
+
},
|
|
568
|
+
]"
|
|
569
|
+
/>
|
|
570
|
+
<ShortcutsHint />
|
|
571
|
+
</div>
|
|
572
|
+
|
|
573
|
+
<!-- No dependencies/items-fn: `expenses` is a page prop, selection
|
|
574
|
+
(across pages) binds straight onto the keyed table context, and
|
|
575
|
+
highlight is a plain local ref since the server never sends it. -->
|
|
576
|
+
<BbTable
|
|
577
|
+
v-model="ctx.table.selected.value"
|
|
578
|
+
v-model:highlighted="ctx.highlighted.value"
|
|
579
|
+
v-model:select-all="ctx.table.all.value"
|
|
580
|
+
v-model:unselected-items="ctx.table.unselected.value"
|
|
581
|
+
actions
|
|
582
|
+
caption="Expense approvals"
|
|
583
|
+
:columns="columns"
|
|
584
|
+
compact
|
|
585
|
+
item-value="id"
|
|
586
|
+
:items="ctx.table.items.value"
|
|
587
|
+
keyboard-navigation
|
|
588
|
+
loading-text="Loading expenses…"
|
|
589
|
+
:row-class="rowClass"
|
|
590
|
+
selectable
|
|
591
|
+
>
|
|
592
|
+
<template #receipt="{ item }">
|
|
593
|
+
<ReceiptThumbnail :expense="item" />
|
|
594
|
+
</template>
|
|
595
|
+
|
|
596
|
+
<template #merchant="{ content }">
|
|
597
|
+
<span class="font-medium">{{ content }}</span>
|
|
598
|
+
</template>
|
|
599
|
+
|
|
600
|
+
<template #submitter_name="{ item }">
|
|
601
|
+
<BbButton
|
|
602
|
+
size="sm"
|
|
603
|
+
variant="ghost"
|
|
604
|
+
@click="() => ctx.stackSubmitter(item.submitter.id)"
|
|
605
|
+
>
|
|
606
|
+
{{ item.submitter.name }}
|
|
607
|
+
</BbButton>
|
|
608
|
+
</template>
|
|
609
|
+
|
|
610
|
+
<template #category="{ item }">
|
|
611
|
+
<BbBadge variant="outline">{{ item.category }}</BbBadge>
|
|
612
|
+
</template>
|
|
613
|
+
|
|
614
|
+
<template #status="{ item }">
|
|
615
|
+
<BbBadge variant="outline">{{ statusLabel(item.status) }}</BbBadge>
|
|
616
|
+
</template>
|
|
617
|
+
|
|
618
|
+
<template #actions="{ item }">
|
|
619
|
+
<RowActions :expense="item" />
|
|
620
|
+
</template>
|
|
621
|
+
|
|
622
|
+
<template #no-data>
|
|
623
|
+
<div class="flex flex-col items-center gap-2 p-6 text-center">
|
|
624
|
+
<p class="text-sm text-gray-500">
|
|
625
|
+
No expenses match
|
|
626
|
+
{{
|
|
627
|
+
ctx.hasActiveFilters.value ? 'the current filters' : 'this tab'
|
|
628
|
+
}}.
|
|
629
|
+
</p>
|
|
630
|
+
<BbButton
|
|
631
|
+
v-if="ctx.hasActiveFilters.value"
|
|
632
|
+
variant="ghost"
|
|
633
|
+
@click="ctx.clearFilters"
|
|
634
|
+
>
|
|
635
|
+
Clear filters
|
|
636
|
+
</BbButton>
|
|
637
|
+
</div>
|
|
638
|
+
</template>
|
|
639
|
+
</BbTable>
|
|
640
|
+
|
|
641
|
+
<!-- No table-id: the keyed table context is the page count here. -->
|
|
642
|
+
<BbPagination
|
|
643
|
+
v-model="ctx.table.page.value"
|
|
644
|
+
label="Expense approvals pagination"
|
|
645
|
+
:total-pages="ctx.table.totalPages.value"
|
|
646
|
+
/>
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<SelectionBar />
|
|
650
|
+
|
|
651
|
+
<!-- Stacked peek sidebar: highlight opens the expense; names drill down. -->
|
|
652
|
+
<template #sidebar>
|
|
653
|
+
<ExpensePeek
|
|
654
|
+
v-if="ctx.shell.current.value?.type === 'expense'"
|
|
655
|
+
:expense-id="ctx.shell.current.value.id"
|
|
656
|
+
/>
|
|
657
|
+
<SubmitterPeek
|
|
658
|
+
v-else-if="ctx.shell.current.value?.type === 'submitter'"
|
|
659
|
+
:submitter-id="ctx.shell.current.value.id"
|
|
660
|
+
/>
|
|
661
|
+
</template>
|
|
662
|
+
|
|
663
|
+
<template #dialogs>
|
|
664
|
+
<RejectDialog />
|
|
665
|
+
<BulkRejectDialog />
|
|
666
|
+
<SnoozeDialog />
|
|
667
|
+
</template>
|
|
668
|
+
</PageShell>
|
|
669
|
+
</template>
|
|
670
|
+
|
|
671
|
+
<script setup lang="ts">
|
|
672
|
+
import {
|
|
673
|
+
BbBadge,
|
|
674
|
+
BbBreadcrumbs,
|
|
675
|
+
BbButton,
|
|
676
|
+
BbPagination,
|
|
677
|
+
BbTable,
|
|
678
|
+
BbTabs,
|
|
679
|
+
BbTextInput,
|
|
680
|
+
} from 'bitboss-ui';
|
|
681
|
+
import PageShell from '@/components/layout/PageShell.vue';
|
|
682
|
+
import { provideApprovalsInbox } from './context';
|
|
683
|
+
import BulkRejectDialog from './BulkRejectDialog.vue';
|
|
684
|
+
import ExpensePeek from './ExpensePeek.vue';
|
|
685
|
+
import FilterRail from './FilterRail.vue';
|
|
686
|
+
import ReceiptThumbnail from './ReceiptThumbnail.vue';
|
|
687
|
+
import RejectDialog from './RejectDialog.vue';
|
|
688
|
+
import RowActions from './RowActions.vue';
|
|
689
|
+
import SelectionBar from './SelectionBar.vue';
|
|
690
|
+
import ShortcutsHint from './ShortcutsHint.vue';
|
|
691
|
+
import SnoozeDialog from './SnoozeDialog.vue';
|
|
692
|
+
import SubmitterPeek from './SubmitterPeek.vue';
|
|
693
|
+
import type { Expense, ExpenseStatus } from '@/types';
|
|
694
|
+
|
|
695
|
+
const ctx = provideApprovalsInbox();
|
|
696
|
+
|
|
697
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
698
|
+
style: 'currency',
|
|
699
|
+
currency: 'USD',
|
|
700
|
+
});
|
|
701
|
+
const shortDate = (value: string) =>
|
|
702
|
+
new Date(value).toLocaleDateString('en-US', {
|
|
703
|
+
month: 'short',
|
|
704
|
+
day: 'numeric',
|
|
705
|
+
});
|
|
706
|
+
const statusLabel = (status: ExpenseStatus) =>
|
|
707
|
+
({
|
|
708
|
+
needsApproval: 'Needs approval',
|
|
709
|
+
approved: 'Approved',
|
|
710
|
+
rejected: 'Rejected',
|
|
711
|
+
snoozed: 'Snoozed',
|
|
712
|
+
})[status];
|
|
713
|
+
|
|
714
|
+
const columns = [
|
|
715
|
+
{ key: 'receipt', label: 'Receipt', width: 48 },
|
|
716
|
+
{ key: 'merchant', label: 'Merchant' },
|
|
717
|
+
{ key: 'submitter.name', label: 'Submitter' },
|
|
718
|
+
{ key: 'category', label: 'Category' },
|
|
719
|
+
{
|
|
720
|
+
key: 'amount',
|
|
721
|
+
label: 'Amount',
|
|
722
|
+
align: 'right' as const,
|
|
723
|
+
formatter: (value: number) => currency.format(value),
|
|
724
|
+
},
|
|
725
|
+
{ key: 'submittedAt', label: 'Submitted', formatter: shortDate },
|
|
726
|
+
{ key: 'status', label: 'Status' },
|
|
727
|
+
];
|
|
728
|
+
|
|
729
|
+
// Highlight ships unstyled — paint it via the public row-class API.
|
|
730
|
+
const rowClass = (item: Expense) =>
|
|
731
|
+
ctx.highlighted.value === item.id
|
|
732
|
+
? 'bg-indigo-50 dark:bg-indigo-950/20'
|
|
733
|
+
: undefined;
|
|
734
|
+
</script>
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### FilterRail.vue
|
|
738
|
+
|
|
739
|
+
```vue
|
|
740
|
+
<!-- ApprovalsInbox/FilterRail.vue -->
|
|
741
|
+
<template>
|
|
742
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
743
|
+
<!-- Submitter: single, query-aware search against a small JSON endpoint. -->
|
|
744
|
+
<BbSelectPopover
|
|
745
|
+
v-model="ctx.filters.submitterId"
|
|
746
|
+
item-text="name"
|
|
747
|
+
item-value="id"
|
|
748
|
+
:items="searchSubmitters"
|
|
749
|
+
>
|
|
750
|
+
<template #activator="{ props }">
|
|
751
|
+
<BbButton
|
|
752
|
+
v-bind="props"
|
|
753
|
+
append:icon="lucide:chevron-down"
|
|
754
|
+
size="sm"
|
|
755
|
+
variant="outline"
|
|
756
|
+
>
|
|
757
|
+
{{ submitterLabel }}
|
|
758
|
+
</BbButton>
|
|
759
|
+
</template>
|
|
760
|
+
</BbSelectPopover>
|
|
761
|
+
|
|
762
|
+
<!-- Category: multi-select pill. -->
|
|
763
|
+
<BbSelectPopover
|
|
764
|
+
v-model="ctx.filters.categories"
|
|
765
|
+
:items="['travel', 'meals', 'software', 'office', 'client', 'other']"
|
|
766
|
+
multiple
|
|
767
|
+
>
|
|
768
|
+
<template #activator="{ props }">
|
|
769
|
+
<BbButton
|
|
770
|
+
v-bind="props"
|
|
771
|
+
append:icon="lucide:chevron-down"
|
|
772
|
+
size="sm"
|
|
773
|
+
variant="outline"
|
|
774
|
+
>
|
|
775
|
+
Category{{
|
|
776
|
+
ctx.filters.categories.length
|
|
777
|
+
? ` · ${ctx.filters.categories.length}`
|
|
778
|
+
: ''
|
|
779
|
+
}}
|
|
780
|
+
</BbButton>
|
|
781
|
+
</template>
|
|
782
|
+
</BbSelectPopover>
|
|
783
|
+
|
|
784
|
+
<!-- Amount over: a quick-filter pill from preset thresholds. -->
|
|
785
|
+
<BbSelectPopover
|
|
786
|
+
v-model="ctx.filters.amountOver"
|
|
787
|
+
disable-writing
|
|
788
|
+
:items="[50, 100, 250, 500, 1000]"
|
|
789
|
+
width="160px"
|
|
790
|
+
>
|
|
791
|
+
<template #activator="{ props }">
|
|
792
|
+
<BbButton
|
|
793
|
+
v-bind="props"
|
|
794
|
+
append:icon="lucide:chevron-down"
|
|
795
|
+
size="sm"
|
|
796
|
+
variant="outline"
|
|
797
|
+
>
|
|
798
|
+
{{
|
|
799
|
+
ctx.filters.amountOver
|
|
800
|
+
? `Over $${ctx.filters.amountOver}`
|
|
801
|
+
: 'Amount'
|
|
802
|
+
}}
|
|
803
|
+
</BbButton>
|
|
804
|
+
</template>
|
|
805
|
+
</BbSelectPopover>
|
|
806
|
+
|
|
807
|
+
<!-- Clear-all renders only while something is active. -->
|
|
808
|
+
<BbButton
|
|
809
|
+
v-if="ctx.hasActiveFilters.value"
|
|
810
|
+
class="ml-auto"
|
|
811
|
+
prepend:icon="lucide:x"
|
|
812
|
+
size="sm"
|
|
813
|
+
variant="ghost"
|
|
814
|
+
@click="ctx.clearFilters"
|
|
815
|
+
>
|
|
816
|
+
Clear filters
|
|
817
|
+
</BbButton>
|
|
818
|
+
</div>
|
|
819
|
+
</template>
|
|
820
|
+
|
|
821
|
+
<script setup lang="ts">
|
|
822
|
+
import { computed, ref, watchEffect } from 'vue';
|
|
823
|
+
import { BbButton, BbSelectPopover } from 'bitboss-ui';
|
|
824
|
+
import { useApprovalsInbox } from './context';
|
|
825
|
+
import type { Submitter } from '@/types';
|
|
826
|
+
|
|
827
|
+
const ctx = useApprovalsInbox();
|
|
828
|
+
|
|
829
|
+
// Query-aware items function: BbSelectPopover calls it with the search text.
|
|
830
|
+
// A small JSON endpoint, not an Inertia visit — searching a filter pill
|
|
831
|
+
// must not re-render the page.
|
|
832
|
+
const searchSubmitters = (query: string): Promise<Submitter[]> =>
|
|
833
|
+
fetch(`/submitters/search?q=${encodeURIComponent(query)}`).then((res) =>
|
|
834
|
+
res.json()
|
|
835
|
+
);
|
|
836
|
+
|
|
837
|
+
// The trigger labels itself from page state (the popover is lazy by design
|
|
838
|
+
// and cannot know the selected label before its first open).
|
|
839
|
+
const selectedSubmitter = ref<Submitter | null>(null);
|
|
840
|
+
watchEffect(async () => {
|
|
841
|
+
selectedSubmitter.value =
|
|
842
|
+
ctx.filters.submitterId == null
|
|
843
|
+
? null
|
|
844
|
+
: await fetch(`/submitters/${ctx.filters.submitterId}/peek`).then((res) =>
|
|
845
|
+
res.json()
|
|
846
|
+
);
|
|
847
|
+
});
|
|
848
|
+
const submitterLabel = computed(
|
|
849
|
+
() => selectedSubmitter.value?.name ?? 'Submitter'
|
|
850
|
+
);
|
|
851
|
+
</script>
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
### ShortcutsHint.vue
|
|
855
|
+
|
|
856
|
+
```vue
|
|
857
|
+
<!-- ApprovalsInbox/ShortcutsHint.vue -->
|
|
858
|
+
<template>
|
|
859
|
+
<div class="flex items-center gap-1">
|
|
860
|
+
<span class="mr-1 text-xs text-gray-500">Shortcuts</span>
|
|
861
|
+
<BbTooltip>
|
|
862
|
+
<template #activator="{ props }">
|
|
863
|
+
<BbButton
|
|
864
|
+
v-bind="props"
|
|
865
|
+
aria-label="Approve highlighted expense"
|
|
866
|
+
:disabled="!ctx.highlightedExpense.value"
|
|
867
|
+
icon="lucide:check"
|
|
868
|
+
size="xs"
|
|
869
|
+
variant="ghost"
|
|
870
|
+
@click="ctx.approveHighlighted"
|
|
871
|
+
/>
|
|
872
|
+
</template>
|
|
873
|
+
<span class="flex items-center gap-1.5">
|
|
874
|
+
Approve
|
|
875
|
+
<kbd
|
|
876
|
+
class="rounded border border-current px-1 font-mono text-[10px] leading-4 opacity-75"
|
|
877
|
+
>A</kbd
|
|
878
|
+
>
|
|
879
|
+
</span>
|
|
880
|
+
</BbTooltip>
|
|
881
|
+
<BbTooltip>
|
|
882
|
+
<template #activator="{ props }">
|
|
883
|
+
<BbButton
|
|
884
|
+
v-bind="props"
|
|
885
|
+
aria-label="Reject highlighted expense"
|
|
886
|
+
:disabled="!ctx.highlightedExpense.value"
|
|
887
|
+
icon="lucide:x"
|
|
888
|
+
size="xs"
|
|
889
|
+
variant="ghost"
|
|
890
|
+
@click="ctx.rejectHighlighted"
|
|
891
|
+
/>
|
|
892
|
+
</template>
|
|
893
|
+
<span class="flex items-center gap-1.5">
|
|
894
|
+
Reject
|
|
895
|
+
<kbd
|
|
896
|
+
class="rounded border border-current px-1 font-mono text-[10px] leading-4 opacity-75"
|
|
897
|
+
>R</kbd
|
|
898
|
+
>
|
|
899
|
+
</span>
|
|
900
|
+
</BbTooltip>
|
|
901
|
+
<BbTooltip>
|
|
902
|
+
<template #activator="{ props }">
|
|
903
|
+
<BbButton
|
|
904
|
+
v-bind="props"
|
|
905
|
+
aria-label="Snooze highlighted expense"
|
|
906
|
+
:disabled="!ctx.highlightedExpense.value"
|
|
907
|
+
icon="lucide:clock"
|
|
908
|
+
size="xs"
|
|
909
|
+
variant="ghost"
|
|
910
|
+
@click="ctx.snoozeHighlighted"
|
|
911
|
+
/>
|
|
912
|
+
</template>
|
|
913
|
+
<span class="flex items-center gap-1.5">
|
|
914
|
+
Snooze
|
|
915
|
+
<kbd
|
|
916
|
+
class="rounded border border-current px-1 font-mono text-[10px] leading-4 opacity-75"
|
|
917
|
+
>S</kbd
|
|
918
|
+
>
|
|
919
|
+
</span>
|
|
920
|
+
</BbTooltip>
|
|
921
|
+
</div>
|
|
922
|
+
</template>
|
|
923
|
+
|
|
924
|
+
<script setup lang="ts">
|
|
925
|
+
import { BbButton, BbTooltip } from 'bitboss-ui';
|
|
926
|
+
import { useApprovalsInbox } from './context';
|
|
927
|
+
|
|
928
|
+
const ctx = useApprovalsInbox();
|
|
929
|
+
</script>
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
### ReceiptThumbnail.vue
|
|
933
|
+
|
|
934
|
+
```vue
|
|
935
|
+
<!-- ApprovalsInbox/ReceiptThumbnail.vue -->
|
|
936
|
+
<template>
|
|
937
|
+
<img
|
|
938
|
+
v-if="expense.receiptUrl"
|
|
939
|
+
:alt="`Receipt from ${expense.merchant}`"
|
|
940
|
+
class="h-8 w-8 rounded-[var(--bb-radius)] border border-gray-200 object-cover dark:border-gray-700"
|
|
941
|
+
:src="expense.receiptUrl"
|
|
942
|
+
/>
|
|
943
|
+
<div
|
|
944
|
+
v-else
|
|
945
|
+
aria-hidden="true"
|
|
946
|
+
class="flex h-8 w-8 items-center justify-center rounded-[var(--bb-radius)] border border-dashed border-gray-300 text-gray-400 dark:border-gray-600"
|
|
947
|
+
>
|
|
948
|
+
<BbIcon icon="lucide:receipt" size="14" />
|
|
949
|
+
</div>
|
|
950
|
+
</template>
|
|
951
|
+
|
|
952
|
+
<script setup lang="ts">
|
|
953
|
+
import { BbIcon } from 'bitboss-ui';
|
|
954
|
+
import type { Expense } from '@/types';
|
|
955
|
+
|
|
956
|
+
defineProps<{ expense: Expense }>();
|
|
957
|
+
</script>
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
### RowActions.vue
|
|
961
|
+
|
|
962
|
+
```vue
|
|
963
|
+
<!-- ApprovalsInbox/RowActions.vue -->
|
|
964
|
+
<template>
|
|
965
|
+
<div class="flex items-center gap-1">
|
|
966
|
+
<BbTooltip text="Approve">
|
|
967
|
+
<template #activator="{ props }">
|
|
968
|
+
<BbButton
|
|
969
|
+
v-bind="props"
|
|
970
|
+
aria-label="Approve"
|
|
971
|
+
:disabled="resolved"
|
|
972
|
+
icon="lucide:check"
|
|
973
|
+
size="xs"
|
|
974
|
+
variant="ghost"
|
|
975
|
+
@click="() => ctx.approve(expense)"
|
|
976
|
+
/>
|
|
977
|
+
</template>
|
|
978
|
+
</BbTooltip>
|
|
979
|
+
<BbTooltip text="Reject">
|
|
980
|
+
<template #activator="{ props }">
|
|
981
|
+
<BbButton
|
|
982
|
+
v-bind="props"
|
|
983
|
+
aria-label="Reject"
|
|
984
|
+
:disabled="resolved"
|
|
985
|
+
icon="lucide:x"
|
|
986
|
+
size="xs"
|
|
987
|
+
variant="ghost"
|
|
988
|
+
@click="() => ctx.openReject(expense)"
|
|
989
|
+
/>
|
|
990
|
+
</template>
|
|
991
|
+
</BbTooltip>
|
|
992
|
+
<BbTooltip text="Snooze">
|
|
993
|
+
<template #activator="{ props }">
|
|
994
|
+
<BbButton
|
|
995
|
+
v-bind="props"
|
|
996
|
+
aria-label="Snooze"
|
|
997
|
+
:disabled="resolved"
|
|
998
|
+
icon="lucide:clock"
|
|
999
|
+
size="xs"
|
|
1000
|
+
variant="ghost"
|
|
1001
|
+
@click="() => ctx.openSnooze(expense)"
|
|
1002
|
+
/>
|
|
1003
|
+
</template>
|
|
1004
|
+
</BbTooltip>
|
|
1005
|
+
</div>
|
|
1006
|
+
</template>
|
|
1007
|
+
|
|
1008
|
+
<script setup lang="ts">
|
|
1009
|
+
import { computed } from 'vue';
|
|
1010
|
+
import { BbButton, BbTooltip } from 'bitboss-ui';
|
|
1011
|
+
import { useApprovalsInbox } from './context';
|
|
1012
|
+
import type { Expense } from '@/types';
|
|
1013
|
+
|
|
1014
|
+
const props = defineProps<{ expense: Expense }>();
|
|
1015
|
+
|
|
1016
|
+
const ctx = useApprovalsInbox();
|
|
1017
|
+
|
|
1018
|
+
const resolved = computed(
|
|
1019
|
+
() =>
|
|
1020
|
+
props.expense.status === 'approved' || props.expense.status === 'rejected'
|
|
1021
|
+
);
|
|
1022
|
+
</script>
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
### RejectDialog.vue
|
|
1026
|
+
|
|
1027
|
+
```vue
|
|
1028
|
+
<!-- ApprovalsInbox/RejectDialog.vue -->
|
|
1029
|
+
<template>
|
|
1030
|
+
<BbDialog
|
|
1031
|
+
:model-value="expense !== null"
|
|
1032
|
+
size="sm"
|
|
1033
|
+
:title="expense ? `Reject — ${expense.merchant}` : ''"
|
|
1034
|
+
@show="reason = ''"
|
|
1035
|
+
@update:model-value="close"
|
|
1036
|
+
>
|
|
1037
|
+
<form
|
|
1038
|
+
v-if="expense"
|
|
1039
|
+
class="flex flex-col gap-3"
|
|
1040
|
+
novalidate
|
|
1041
|
+
@submit.prevent="submit"
|
|
1042
|
+
>
|
|
1043
|
+
<BbTextarea
|
|
1044
|
+
v-model="reason"
|
|
1045
|
+
label="Reason"
|
|
1046
|
+
label-mode="outside"
|
|
1047
|
+
placeholder="Why is this expense being rejected?"
|
|
1048
|
+
rows="3"
|
|
1049
|
+
/>
|
|
1050
|
+
<div class="mt-1 flex justify-end gap-2">
|
|
1051
|
+
<BbButton type="button" variant="ghost" @click="close">Cancel</BbButton>
|
|
1052
|
+
<BbButton
|
|
1053
|
+
:disabled="reason.trim().length < 3"
|
|
1054
|
+
type="submit"
|
|
1055
|
+
variant="destructive"
|
|
1056
|
+
>
|
|
1057
|
+
Reject expense
|
|
1058
|
+
</BbButton>
|
|
1059
|
+
</div>
|
|
1060
|
+
</form>
|
|
1061
|
+
</BbDialog>
|
|
1062
|
+
</template>
|
|
1063
|
+
|
|
1064
|
+
<script setup lang="ts">
|
|
1065
|
+
import { computed, ref } from 'vue';
|
|
1066
|
+
import { BbButton, BbDialog, BbTextarea } from 'bitboss-ui';
|
|
1067
|
+
import { useApprovalsInbox } from './context';
|
|
1068
|
+
|
|
1069
|
+
const ctx = useApprovalsInbox();
|
|
1070
|
+
|
|
1071
|
+
// The dialog's subject lives in the page context — the row action and the
|
|
1072
|
+
// peek's reject button both just assign it.
|
|
1073
|
+
const expense = computed(() => ctx.dialogs.reject.value);
|
|
1074
|
+
const close = () => {
|
|
1075
|
+
ctx.dialogs.reject.value = null;
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
const reason = ref('');
|
|
1079
|
+
const submit = () =>
|
|
1080
|
+
expense.value ? ctx.reject(expense.value, reason.value.trim()) : undefined;
|
|
1081
|
+
</script>
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### BulkRejectDialog.vue
|
|
1085
|
+
|
|
1086
|
+
```vue
|
|
1087
|
+
<!-- ApprovalsInbox/BulkRejectDialog.vue -->
|
|
1088
|
+
<template>
|
|
1089
|
+
<BbDialog
|
|
1090
|
+
:model-value="ctx.dialogs.bulkReject.value"
|
|
1091
|
+
size="sm"
|
|
1092
|
+
:title="`Reject ${count} expenses`"
|
|
1093
|
+
@show="reason = ''"
|
|
1094
|
+
@update:model-value="close"
|
|
1095
|
+
>
|
|
1096
|
+
<form class="flex flex-col gap-3" novalidate @submit.prevent="submit">
|
|
1097
|
+
<BbTextarea
|
|
1098
|
+
v-model="reason"
|
|
1099
|
+
label="Reason"
|
|
1100
|
+
label-mode="outside"
|
|
1101
|
+
placeholder="Applied to every selected expense."
|
|
1102
|
+
rows="3"
|
|
1103
|
+
/>
|
|
1104
|
+
<div class="mt-1 flex justify-end gap-2">
|
|
1105
|
+
<BbButton type="button" variant="ghost" @click="close">Cancel</BbButton>
|
|
1106
|
+
<BbButton
|
|
1107
|
+
:disabled="reason.trim().length < 3"
|
|
1108
|
+
type="submit"
|
|
1109
|
+
variant="destructive"
|
|
1110
|
+
>
|
|
1111
|
+
Reject {{ count }} expenses
|
|
1112
|
+
</BbButton>
|
|
1113
|
+
</div>
|
|
1114
|
+
</form>
|
|
1115
|
+
</BbDialog>
|
|
1116
|
+
</template>
|
|
1117
|
+
|
|
1118
|
+
<script setup lang="ts">
|
|
1119
|
+
import { computed, ref } from 'vue';
|
|
1120
|
+
import { BbButton, BbDialog, BbTextarea } from 'bitboss-ui';
|
|
1121
|
+
import { useApprovalsInbox } from './context';
|
|
1122
|
+
|
|
1123
|
+
const ctx = useApprovalsInbox();
|
|
1124
|
+
|
|
1125
|
+
const count = computed(() => (ctx.table.selected.value as number[]).length);
|
|
1126
|
+
const close = () => {
|
|
1127
|
+
ctx.dialogs.bulkReject.value = false;
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
const reason = ref('');
|
|
1131
|
+
const submit = () => ctx.bulkReject(reason.value.trim());
|
|
1132
|
+
</script>
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
### SnoozeDialog.vue
|
|
1136
|
+
|
|
1137
|
+
```vue
|
|
1138
|
+
<!-- ApprovalsInbox/SnoozeDialog.vue -->
|
|
1139
|
+
<template>
|
|
1140
|
+
<BbDialog
|
|
1141
|
+
:model-value="expense !== null"
|
|
1142
|
+
size="xs"
|
|
1143
|
+
:title="expense ? `Snooze — ${expense.merchant}` : ''"
|
|
1144
|
+
@show="until = tomorrow"
|
|
1145
|
+
@update:model-value="close"
|
|
1146
|
+
>
|
|
1147
|
+
<form
|
|
1148
|
+
v-if="expense"
|
|
1149
|
+
class="flex flex-col gap-3"
|
|
1150
|
+
novalidate
|
|
1151
|
+
@submit.prevent="submit"
|
|
1152
|
+
>
|
|
1153
|
+
<BbDatePickerInput
|
|
1154
|
+
v-model="until"
|
|
1155
|
+
floating
|
|
1156
|
+
label="Snooze until"
|
|
1157
|
+
label-mode="outside"
|
|
1158
|
+
:min="tomorrow"
|
|
1159
|
+
/>
|
|
1160
|
+
<div class="mt-1 flex justify-end gap-2">
|
|
1161
|
+
<BbButton type="button" variant="ghost" @click="close">Cancel</BbButton>
|
|
1162
|
+
<BbButton type="submit" variant="secondary">Snooze</BbButton>
|
|
1163
|
+
</div>
|
|
1164
|
+
</form>
|
|
1165
|
+
</BbDialog>
|
|
1166
|
+
</template>
|
|
1167
|
+
|
|
1168
|
+
<script setup lang="ts">
|
|
1169
|
+
import { computed, ref } from 'vue';
|
|
1170
|
+
import { BbButton, BbDatePickerInput, BbDialog } from 'bitboss-ui';
|
|
1171
|
+
import { useApprovalsInbox } from './context';
|
|
1172
|
+
|
|
1173
|
+
const ctx = useApprovalsInbox();
|
|
1174
|
+
|
|
1175
|
+
const expense = computed(() => ctx.dialogs.snooze.value);
|
|
1176
|
+
const close = () => {
|
|
1177
|
+
ctx.dialogs.snooze.value = null;
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
const tomorrow = new Date(Date.now() + 86_400_000).toISOString().slice(0, 10);
|
|
1181
|
+
const until = ref(tomorrow);
|
|
1182
|
+
const submit = () =>
|
|
1183
|
+
expense.value ? ctx.snooze(expense.value, until.value) : undefined;
|
|
1184
|
+
</script>
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
### SelectionBar.vue
|
|
1188
|
+
|
|
1189
|
+
```vue
|
|
1190
|
+
<!-- ApprovalsInbox/SelectionBar.vue -->
|
|
1191
|
+
<template>
|
|
1192
|
+
<FloatingBottomPanel :open="selectedCount > 0">
|
|
1193
|
+
<span class="px-2 text-sm">{{ selectedCount }} selected</span>
|
|
1194
|
+
<BbButton
|
|
1195
|
+
prepend:icon="lucide:check"
|
|
1196
|
+
size="sm"
|
|
1197
|
+
variant="ghost"
|
|
1198
|
+
@click="ctx.bulkApprove"
|
|
1199
|
+
>
|
|
1200
|
+
Approve
|
|
1201
|
+
</BbButton>
|
|
1202
|
+
<div class="floating-bottom-panel__right flex items-center gap-1">
|
|
1203
|
+
<BbButton size="sm" variant="destructive" @click="ctx.openBulkReject">
|
|
1204
|
+
Reject
|
|
1205
|
+
</BbButton>
|
|
1206
|
+
<BbButton
|
|
1207
|
+
aria-label="Clear selection"
|
|
1208
|
+
icon="lucide:x"
|
|
1209
|
+
size="sm"
|
|
1210
|
+
variant="ghost"
|
|
1211
|
+
@click="ctx.table.resetSelection"
|
|
1212
|
+
/>
|
|
1213
|
+
</div>
|
|
1214
|
+
</FloatingBottomPanel>
|
|
1215
|
+
</template>
|
|
1216
|
+
|
|
1217
|
+
<script setup lang="ts">
|
|
1218
|
+
import { computed } from 'vue';
|
|
1219
|
+
import { BbButton } from 'bitboss-ui';
|
|
1220
|
+
import { useApprovalsInbox } from './context';
|
|
1221
|
+
import FloatingBottomPanel from '@/components/layout/FloatingBottomPanel.vue';
|
|
1222
|
+
|
|
1223
|
+
const ctx = useApprovalsInbox();
|
|
1224
|
+
|
|
1225
|
+
// `all` (select-all-across-pages) does not enumerate rows, so the visible
|
|
1226
|
+
// count is the total minus whatever the user manually deselected.
|
|
1227
|
+
const selectedCount = computed(() =>
|
|
1228
|
+
ctx.table.all.value
|
|
1229
|
+
? ctx.table.totalItems.value - ctx.table.unselected.value.length
|
|
1230
|
+
: ctx.table.selected.value.length
|
|
1231
|
+
);
|
|
1232
|
+
</script>
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
### ExpensePeek.vue
|
|
1236
|
+
|
|
1237
|
+
```vue
|
|
1238
|
+
<!-- ApprovalsInbox/ExpensePeek.vue -->
|
|
1239
|
+
<template>
|
|
1240
|
+
<div v-if="expense" class="flex flex-col gap-4 text-sm">
|
|
1241
|
+
<!-- Header: identity + amount + status. -->
|
|
1242
|
+
<header class="flex flex-col gap-1.5">
|
|
1243
|
+
<div class="flex items-center justify-between gap-2">
|
|
1244
|
+
<h2 class="text-base font-semibold">{{ expense.merchant }}</h2>
|
|
1245
|
+
<BbBadge variant="outline">{{ statusLabel(expense.status) }}</BbBadge>
|
|
1246
|
+
</div>
|
|
1247
|
+
<p class="text-xl font-semibold">{{ currency.format(expense.amount) }}</p>
|
|
1248
|
+
<p class="text-xs text-gray-500">
|
|
1249
|
+
Submitted {{ longDate(expense.submittedAt) }}
|
|
1250
|
+
</p>
|
|
1251
|
+
</header>
|
|
1252
|
+
|
|
1253
|
+
<!-- Receipt: bigger than the row thumbnail, with a full-size link out. -->
|
|
1254
|
+
<section class="flex flex-col gap-1.5">
|
|
1255
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1256
|
+
Receipt
|
|
1257
|
+
</h3>
|
|
1258
|
+
<img
|
|
1259
|
+
v-if="expense.receiptUrl"
|
|
1260
|
+
:alt="`Receipt from ${expense.merchant}`"
|
|
1261
|
+
class="h-40 w-full rounded-[var(--bb-radius)] border border-gray-200 object-cover dark:border-gray-700"
|
|
1262
|
+
:src="expense.receiptUrl"
|
|
1263
|
+
/>
|
|
1264
|
+
<p v-else class="text-xs text-gray-500">No receipt attached.</p>
|
|
1265
|
+
<BbButton
|
|
1266
|
+
v-if="expense.receiptUrl"
|
|
1267
|
+
append:icon="lucide:arrow-up-right"
|
|
1268
|
+
:href="expense.receiptUrl"
|
|
1269
|
+
size="sm"
|
|
1270
|
+
target="_blank"
|
|
1271
|
+
variant="ghost"
|
|
1272
|
+
>
|
|
1273
|
+
View full-size receipt
|
|
1274
|
+
</BbButton>
|
|
1275
|
+
</section>
|
|
1276
|
+
|
|
1277
|
+
<!-- Properties: submitter name drills DOWN — stacked peek, back arrow. -->
|
|
1278
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5">
|
|
1279
|
+
<dt class="text-gray-500">Submitter</dt>
|
|
1280
|
+
<dd>
|
|
1281
|
+
<BbButton
|
|
1282
|
+
size="sm"
|
|
1283
|
+
variant="link"
|
|
1284
|
+
@click="() => ctx.stackSubmitter(expense!.submitter.id)"
|
|
1285
|
+
>
|
|
1286
|
+
{{ expense.submitter.name }}
|
|
1287
|
+
</BbButton>
|
|
1288
|
+
</dd>
|
|
1289
|
+
<dt class="text-gray-500">Category</dt>
|
|
1290
|
+
<dd class="capitalize">{{ expense.category }}</dd>
|
|
1291
|
+
<dt class="text-gray-500">Memo</dt>
|
|
1292
|
+
<dd>{{ expense.memo ?? '—' }}</dd>
|
|
1293
|
+
</dl>
|
|
1294
|
+
|
|
1295
|
+
<!-- Activity feed. -->
|
|
1296
|
+
<section class="flex flex-col gap-1.5">
|
|
1297
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1298
|
+
Activity
|
|
1299
|
+
</h3>
|
|
1300
|
+
<ul class="flex flex-col gap-2">
|
|
1301
|
+
<li
|
|
1302
|
+
v-for="entry in expense.activity"
|
|
1303
|
+
:key="entry.id"
|
|
1304
|
+
class="flex gap-2"
|
|
1305
|
+
>
|
|
1306
|
+
<BbIcon
|
|
1307
|
+
class="mt-0.5 shrink-0 text-gray-400"
|
|
1308
|
+
:icon="entry.icon"
|
|
1309
|
+
size="14"
|
|
1310
|
+
/>
|
|
1311
|
+
<div class="flex flex-col">
|
|
1312
|
+
<span>{{ entry.text }}</span>
|
|
1313
|
+
<span class="text-xs text-gray-500"
|
|
1314
|
+
>{{ entry.actor }} · {{ longDate(entry.at) }}</span
|
|
1315
|
+
>
|
|
1316
|
+
</div>
|
|
1317
|
+
</li>
|
|
1318
|
+
</ul>
|
|
1319
|
+
</section>
|
|
1320
|
+
|
|
1321
|
+
<!-- Peek is a workbench: the same three actions as the row, plus the real page. -->
|
|
1322
|
+
<footer class="mt-2 flex flex-wrap items-center gap-2 border-t pt-3">
|
|
1323
|
+
<BbButton
|
|
1324
|
+
:disabled="resolved"
|
|
1325
|
+
prepend:icon="lucide:check"
|
|
1326
|
+
size="sm"
|
|
1327
|
+
variant="secondary"
|
|
1328
|
+
@click="() => ctx.approve(expense!)"
|
|
1329
|
+
>
|
|
1330
|
+
Approve
|
|
1331
|
+
</BbButton>
|
|
1332
|
+
<BbButton
|
|
1333
|
+
:disabled="resolved"
|
|
1334
|
+
prepend:icon="lucide:x"
|
|
1335
|
+
size="sm"
|
|
1336
|
+
variant="destructive"
|
|
1337
|
+
@click="() => ctx.openReject(expense!)"
|
|
1338
|
+
>
|
|
1339
|
+
Reject
|
|
1340
|
+
</BbButton>
|
|
1341
|
+
<BbButton
|
|
1342
|
+
:disabled="resolved"
|
|
1343
|
+
prepend:icon="lucide:clock"
|
|
1344
|
+
size="sm"
|
|
1345
|
+
variant="outline"
|
|
1346
|
+
@click="() => ctx.openSnooze(expense!)"
|
|
1347
|
+
>
|
|
1348
|
+
Snooze
|
|
1349
|
+
</BbButton>
|
|
1350
|
+
<BbButton
|
|
1351
|
+
append:icon="lucide:arrow-up-right"
|
|
1352
|
+
class="ml-auto"
|
|
1353
|
+
:href="`/expenses/${expense.id}`"
|
|
1354
|
+
size="sm"
|
|
1355
|
+
variant="ghost"
|
|
1356
|
+
>
|
|
1357
|
+
Open full expense
|
|
1358
|
+
</BbButton>
|
|
1359
|
+
</footer>
|
|
1360
|
+
</div>
|
|
1361
|
+
|
|
1362
|
+
<div v-else class="flex justify-center p-8">
|
|
1363
|
+
<BbSpinner />
|
|
1364
|
+
</div>
|
|
1365
|
+
</template>
|
|
1366
|
+
|
|
1367
|
+
<script setup lang="ts">
|
|
1368
|
+
import { computed, watch } from 'vue';
|
|
1369
|
+
import { BbBadge, BbButton, BbIcon, BbSpinner } from 'bitboss-ui';
|
|
1370
|
+
import { useResourceContext } from '@/composables/useResourceContext';
|
|
1371
|
+
import { useApprovalsInbox } from './context';
|
|
1372
|
+
import type { ExpenseDetail, ExpenseStatus } from '@/types';
|
|
1373
|
+
|
|
1374
|
+
const props = defineProps<{ expenseId: number }>();
|
|
1375
|
+
|
|
1376
|
+
const ctx = useApprovalsInbox();
|
|
1377
|
+
|
|
1378
|
+
// A small JSON endpoint, not a page visit — opening the peek never
|
|
1379
|
+
// re-renders the list underneath it. Re-fetches whenever the stack shows a
|
|
1380
|
+
// different expense; `refreshKey` makes mutations (approve/reject/snooze)
|
|
1381
|
+
// reflect here immediately.
|
|
1382
|
+
const { data: expense, refresh } = useResourceContext<ExpenseDetail, number>(
|
|
1383
|
+
() => props.expenseId,
|
|
1384
|
+
(id) => fetch(`/expenses/${id}/peek`).then((res) => res.json())
|
|
1385
|
+
);
|
|
1386
|
+
watch(() => ctx.refreshKey.value, refresh);
|
|
1387
|
+
|
|
1388
|
+
const resolved = computed(
|
|
1389
|
+
() =>
|
|
1390
|
+
expense.value?.status === 'approved' || expense.value?.status === 'rejected'
|
|
1391
|
+
);
|
|
1392
|
+
|
|
1393
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1394
|
+
style: 'currency',
|
|
1395
|
+
currency: 'USD',
|
|
1396
|
+
});
|
|
1397
|
+
const longDate = (value: string) =>
|
|
1398
|
+
new Date(value).toLocaleDateString('en-US', {
|
|
1399
|
+
month: 'short',
|
|
1400
|
+
day: 'numeric',
|
|
1401
|
+
year: 'numeric',
|
|
1402
|
+
});
|
|
1403
|
+
const statusLabel = (status: ExpenseStatus) =>
|
|
1404
|
+
({
|
|
1405
|
+
needsApproval: 'Needs approval',
|
|
1406
|
+
approved: 'Approved',
|
|
1407
|
+
rejected: 'Rejected',
|
|
1408
|
+
snoozed: 'Snoozed',
|
|
1409
|
+
})[status];
|
|
1410
|
+
</script>
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
### SubmitterPeek.vue
|
|
1414
|
+
|
|
1415
|
+
```vue
|
|
1416
|
+
<!-- ApprovalsInbox/SubmitterPeek.vue -->
|
|
1417
|
+
<template>
|
|
1418
|
+
<div v-if="submitter" class="flex flex-col gap-4 text-sm">
|
|
1419
|
+
<header class="flex items-center gap-3">
|
|
1420
|
+
<BbAvatar :alt="submitter.name" :src="submitter.avatarUrl" />
|
|
1421
|
+
<div class="flex flex-col">
|
|
1422
|
+
<h2 class="text-base font-semibold">{{ submitter.name }}</h2>
|
|
1423
|
+
<p class="text-xs text-gray-500">
|
|
1424
|
+
{{ submitter.email }} · {{ submitter.team }}
|
|
1425
|
+
</p>
|
|
1426
|
+
</div>
|
|
1427
|
+
</header>
|
|
1428
|
+
|
|
1429
|
+
<!-- Stat strip. -->
|
|
1430
|
+
<div class="grid grid-cols-3 gap-2">
|
|
1431
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1432
|
+
<p class="text-xs text-gray-500">Submitted (YTD)</p>
|
|
1433
|
+
<p class="font-semibold">
|
|
1434
|
+
{{ currency.format(submitter.totalSubmitted) }}
|
|
1435
|
+
</p>
|
|
1436
|
+
</div>
|
|
1437
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1438
|
+
<p class="text-xs text-gray-500">Pending</p>
|
|
1439
|
+
<p class="font-semibold">{{ submitter.pendingCount }}</p>
|
|
1440
|
+
</div>
|
|
1441
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1442
|
+
<p class="text-xs text-gray-500">Approval rate</p>
|
|
1443
|
+
<p class="font-semibold">{{ submitter.approvalRate }}%</p>
|
|
1444
|
+
</div>
|
|
1445
|
+
</div>
|
|
1446
|
+
|
|
1447
|
+
<!-- Recent expenses: names drill down again — the stack keeps growing. -->
|
|
1448
|
+
<section class="flex flex-col gap-1.5">
|
|
1449
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1450
|
+
Recent expenses
|
|
1451
|
+
</h3>
|
|
1452
|
+
<ul class="flex flex-col">
|
|
1453
|
+
<li
|
|
1454
|
+
v-for="recent in submitter.recentExpenses"
|
|
1455
|
+
:key="recent.id"
|
|
1456
|
+
class="flex items-center justify-between gap-2 border-b py-1.5 last:border-b-0"
|
|
1457
|
+
>
|
|
1458
|
+
<BbButton
|
|
1459
|
+
size="sm"
|
|
1460
|
+
variant="link"
|
|
1461
|
+
@click="() => ctx.stackExpense(recent.id)"
|
|
1462
|
+
>
|
|
1463
|
+
{{ recent.merchant }}
|
|
1464
|
+
</BbButton>
|
|
1465
|
+
<BbBadge variant="outline">{{ recent.status }}</BbBadge>
|
|
1466
|
+
<span class="text-xs text-gray-500">{{
|
|
1467
|
+
currency.format(recent.amount)
|
|
1468
|
+
}}</span>
|
|
1469
|
+
</li>
|
|
1470
|
+
</ul>
|
|
1471
|
+
</section>
|
|
1472
|
+
|
|
1473
|
+
<footer class="mt-2 flex items-center gap-2 border-t pt-3">
|
|
1474
|
+
<BbButton
|
|
1475
|
+
append:icon="lucide:arrow-up-right"
|
|
1476
|
+
:href="`/people/${submitter.id}`"
|
|
1477
|
+
size="sm"
|
|
1478
|
+
variant="ghost"
|
|
1479
|
+
>
|
|
1480
|
+
Open profile
|
|
1481
|
+
</BbButton>
|
|
1482
|
+
</footer>
|
|
1483
|
+
</div>
|
|
1484
|
+
|
|
1485
|
+
<div v-else class="flex justify-center p-8">
|
|
1486
|
+
<BbSpinner />
|
|
1487
|
+
</div>
|
|
1488
|
+
</template>
|
|
1489
|
+
|
|
1490
|
+
<script setup lang="ts">
|
|
1491
|
+
import { BbAvatar, BbBadge, BbButton, BbSpinner } from 'bitboss-ui';
|
|
1492
|
+
import { useResourceContext } from '@/composables/useResourceContext';
|
|
1493
|
+
import { useApprovalsInbox } from './context';
|
|
1494
|
+
import type { SubmitterDetail } from '@/types';
|
|
1495
|
+
|
|
1496
|
+
const props = defineProps<{ submitterId: number }>();
|
|
1497
|
+
|
|
1498
|
+
const ctx = useApprovalsInbox();
|
|
1499
|
+
|
|
1500
|
+
const { data: submitter } = useResourceContext<SubmitterDetail, number>(
|
|
1501
|
+
() => props.submitterId,
|
|
1502
|
+
(id) => fetch(`/submitters/${id}/peek`).then((res) => res.json())
|
|
1503
|
+
);
|
|
1504
|
+
|
|
1505
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1506
|
+
style: 'currency',
|
|
1507
|
+
currency: 'USD',
|
|
1508
|
+
});
|
|
1509
|
+
</script>
|
|
1510
|
+
```
|
|
1511
|
+
|
|
1512
|
+
## Why it is built this way
|
|
1513
|
+
|
|
1514
|
+
- **The context coopts, leaves consume.** `shell` and the keyed
|
|
1515
|
+
`useTableContext('expenses')` are joined once in
|
|
1516
|
+
`provideApprovalsInbox()`; twelve components share filters, selection,
|
|
1517
|
+
highlight, the peek stack, the keyboard shortcuts and every action
|
|
1518
|
+
through one `useApprovalsInbox()` call.
|
|
1519
|
+
- **Highlight is a plain local ref, not part of the data context.** Per
|
|
1520
|
+
the [Inertia Helpers](../../guides/inertia-helpers.md) rule of thumb, the
|
|
1521
|
+
keyed data contexts "own what the server sent" — items, pagination,
|
|
1522
|
+
selection — while anything the table widget does on its own (sort,
|
|
1523
|
+
highlight) is not server data. `ctx.highlighted` is bound to `BbTable`'s
|
|
1524
|
+
own `v-model:highlighted` and watched exactly like `table.highlighted`
|
|
1525
|
+
in the Vue/Nuxt recipes, so the peek grammar and the keyboard dispatch
|
|
1526
|
+
are unchanged.
|
|
1527
|
+
- **`expenses` is a page prop, not a client fetch.** `useTableContext`
|
|
1528
|
+
turns the Laravel paginator into `items`/`totalItems`/`page`/`perPage`
|
|
1529
|
+
(URL-backed) plus `selected`/`unselected`/`all`; `BbTable` binds directly
|
|
1530
|
+
to that handle with `v-model`/`v-model:select-all`/`v-model:unselected-items`
|
|
1531
|
+
— there is no items function and no `dependencies` array, because the
|
|
1532
|
+
server already did the filtering, sorting and paging before the props
|
|
1533
|
+
arrived.
|
|
1534
|
+
- **One watcher for filters, one visit mechanism for tabs.**
|
|
1535
|
+
`useQueryObject(filters, { only: ['expenses', 'tabCounts'] })` is the
|
|
1536
|
+
entire filter-to-URL wiring; `BbTabs`' `href` + `server: true` items (with
|
|
1537
|
+
the required component-level `navigation`) make tab switches real,
|
|
1538
|
+
partial-reloaded visits with the same `only` list — one source of
|
|
1539
|
+
server truth, reached two ways.
|
|
1540
|
+
- **The `visit()` helper is the promise-wrapped router-visit pattern**
|
|
1541
|
+
called for by the base recipe's platform notes: it turns Inertia's
|
|
1542
|
+
`onSuccess`/`onError` callbacks into a promise so every context action
|
|
1543
|
+
(`approve`, `reject`, `snooze`, the bulk actions, …) stays an `async
|
|
1544
|
+
function` — identical call sites to the Vue/Nuxt recipes,
|
|
1545
|
+
`await ctx.approve(...)`.
|
|
1546
|
+
- **Bulk actions carry the full select-all-across-pages payload**
|
|
1547
|
+
(`ids`/`all`/`unselected`) so "select all, deselect three, approve" sends
|
|
1548
|
+
the same semantics the table itself models — never just an enumerated id
|
|
1549
|
+
list that silently drops rows outside the current page.
|
|
1550
|
+
- **Peeks are JSON fetches, not visits.** `useResourceContext` re-fetches
|
|
1551
|
+
on id change and exposes a `refresh()` the peek re-runs whenever
|
|
1552
|
+
`ctx.refreshKey` bumps (every mutation bumps it) — opening or updating a
|
|
1553
|
+
peek never re-renders the list page underneath it.
|
|
1554
|
+
- **`item-value="id"`** is what lets `table.selected`/`table.unselected`
|
|
1555
|
+
and `ctx.highlighted` all hold plain ids instead of whole objects — the
|
|
1556
|
+
keyboard handler and the peek both resolve the full `Expense` from
|
|
1557
|
+
`table.items` (the current page's server-sent rows).
|
|
1558
|
+
- **One dispatch path per action, three entry points.** `approve`,
|
|
1559
|
+
`openReject` and `openSnooze` are called identically from `RowActions`,
|
|
1560
|
+
`ExpensePeek`'s footer, and the `A`/`R`/`S` keyboard handlers
|
|
1561
|
+
(`approveHighlighted` etc. are the same functions, resolved against
|
|
1562
|
+
`highlightedExpense` instead of a row prop).
|
|
1563
|
+
- **Reason and date are dialogs, not confirms**, exactly as in the Vue
|
|
1564
|
+
recipe: `ctx.dialogs.reject` holds the subject `Expense`, not a boolean,
|
|
1565
|
+
so the row action and the peek's reject button both just assign it.
|
|
1566
|
+
- **The shortcut guard mirrors `useDeleteCmd`.** `isInteractiveElementFocused()`
|
|
1567
|
+
is copied verbatim from the [Inertia Helpers](../../guides/inertia-helpers.md)
|
|
1568
|
+
guide's `useDeleteCmd` (which the guide notes "works on any platform") so
|
|
1569
|
+
typing a rejection reason never fires `R`; the handler also bails while
|
|
1570
|
+
any dialog is open. This is the natural home to promote the guard into
|
|
1571
|
+
the shared toolkit as a reusable `useSingleLetterCmd`-shaped helper
|
|
1572
|
+
(reported, not patched here — see Report below).
|
|
1573
|
+
- **`peekX` vs `stackX` is the whole stack grammar**, unchanged from the
|
|
1574
|
+
Vue recipe: highlighting always **replaces** (`peekExpense`, `true`),
|
|
1575
|
+
clicking a submitter's name always **pushes** (`stackSubmitter`), and the
|
|
1576
|
+
shell renders the back arrow once depth > 1.
|
|
1577
|
+
- **Sizing:** topbar and rail follow `md`-first; `sm`/`xs` appear exactly
|
|
1578
|
+
where the doctrine allows — table rows, the shortcut bar, the peek
|
|
1579
|
+
footers and the bulk bar.
|
|
1580
|
+
- **No `variant="primary"` on this page.** Triage has no "create" action —
|
|
1581
|
+
every action here is either a one-click mutation, a gated mutation via
|
|
1582
|
+
dialog, a bulk mutation, or navigation.
|
|
1583
|
+
|
|
1584
|
+
## Variations
|
|
1585
|
+
|
|
1586
|
+
- **No snooze**: drop `SnoozeDialog.vue`, the `S` shortcut, and the
|
|
1587
|
+
`snoozed` tab state — the remaining approve/reject pair still clears the
|
|
1588
|
+
≥ 12 action bar on its own via the bulk/keyboard/peek spread.
|
|
1589
|
+
- **Auto-advance after action**: after `approve`/`reject` resolves, set
|
|
1590
|
+
`ctx.highlighted.value = nextExpenseId` (the id following the acted-on
|
|
1591
|
+
row in `table.items.value`) so triage keeps moving without a
|
|
1592
|
+
re-highlight — the classic "Superhuman" flow.
|
|
1593
|
+
- **Per-category routing**: seed `filters.categories` from a saved view
|
|
1594
|
+
per approver role (a store-backed default) instead of starting empty.
|
|
1595
|
+
- **Lazy tab counts**: if `tabCounts` is expensive to compute, share it as
|
|
1596
|
+
`Inertia::lazy()` and read it with `useLazyProp` instead of a plain
|
|
1597
|
+
prop — the tab labels resolve once it lands instead of blocking the
|
|
1598
|
+
first render.
|