bitboss-ui 2.1.135 → 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 +162 -60
- 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,1561 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Approvals Inbox (Nuxt)
|
|
3
|
+
summary: Keyboard-first expense-approvals triage for Nuxt — `keyboard-navigation` with highlight-opens-peek, one-click approve, reject-with-reason and snooze dialogs backed by `useAsyncData`/`$fetch`, bulk approve/reject, and an `A`/`R`/`S` shortcut bar, filters URL-synced, all through one coopting page context.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Recipe: Approvals Inbox (Nuxt)
|
|
7
|
+
|
|
8
|
+
The densest advanced-page recipe in the folder, adapted for **Nuxt**: the
|
|
9
|
+
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
|
+
and an `A`/`R`/`S` shortcut bar — but filters and the active tab live in the
|
|
13
|
+
URL and the list loads through `useAsyncData` watching `route.query`.
|
|
14
|
+
Anatomy, components, the context shape, copy and data are identical to the
|
|
15
|
+
Vue recipe; only the data/URL/mutation layer changes.
|
|
16
|
+
|
|
17
|
+
Sibling recipes: [Vue](../vue/approvals-inbox.md) ·
|
|
18
|
+
[Inertia](../inertia/approvals-inbox.md).
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
- `<BbConfirm />` and `<BbToast />` mounted once in `app.vue`.
|
|
23
|
+
- [App Layout](../../guides/app-layout.md), [Page Shell](../../guides/page-shell.md)
|
|
24
|
+
and [Floating Bottom Panel](../../guides/floating-bottom-panel.md) copied
|
|
25
|
+
into `src/components/layout/` per their guides — every recipe page assumes
|
|
26
|
+
this chrome exists and renders inside it.
|
|
27
|
+
- Server routes: `GET /api/expenses` (accepts `search`, `submitterId`,
|
|
28
|
+
`categories[]`, `amountOver`, `tab`, `page`, `perPage`, returns
|
|
29
|
+
`{ data, total, tabCounts }`), plus the mutation routes referenced below
|
|
30
|
+
(`PATCH /api/expenses/:id/approve`, `POST /api/expenses/:id/reject`,
|
|
31
|
+
`PATCH /api/expenses/:id/snooze`, `PATCH /api/expenses/bulk/approve`,
|
|
32
|
+
`POST /api/expenses/bulk/reject`) and small `GET /api/expenses/:id`,
|
|
33
|
+
`GET /api/submitters`, `GET /api/submitters/:id` endpoints backing the
|
|
34
|
+
peeks and the submitter filter pill.
|
|
35
|
+
|
|
36
|
+
## Key ideas
|
|
37
|
+
|
|
38
|
+
- **Highlight drives the peek, not a click handler.** The table gets
|
|
39
|
+
`keyboard-navigation`; `ctx.table.highlighted` (the coopted **library**
|
|
40
|
+
table context — unchanged from the Vue recipe, since it tracks the table
|
|
41
|
+
widget, not the URL) is watched in `context.ts`, and every highlight
|
|
42
|
+
change replaces the sidebar's stack with that expense — arrowing through
|
|
43
|
+
rows previews each one with zero markup in `Index.vue`. Selection stays a
|
|
44
|
+
fully independent axis, free for the bulk bar.
|
|
45
|
+
- **Filters and tab live in the URL, not in local refs.** They are seeded
|
|
46
|
+
from `route.query` at setup and every change writes back through
|
|
47
|
+
`navigateTo` — the page is bookmarkable and survives reload.
|
|
48
|
+
- **`useAsyncData` watches `route.query`.** That single watch source is the
|
|
49
|
+
whole refetch trigger — filters, tab and page changes all funnel through
|
|
50
|
+
the URL first, so there is exactly one fetch path.
|
|
51
|
+
- **One coopting page context.** `provideApprovalsInbox()` joins the
|
|
52
|
+
page-shell stack and the table context once, owns filters/tab/dialogs,
|
|
53
|
+
and exposes complete actions. Every leaf calls `useApprovalsInbox()` and
|
|
54
|
+
nothing else.
|
|
55
|
+
- **Three dispatch surfaces, one set of actions.** Approve / reject /
|
|
56
|
+
snooze are reachable from the row, from inside the peek, and from the
|
|
57
|
+
`A`/`R`/`S` keyboard shortcuts — all three call the exact same context
|
|
58
|
+
functions (`approve`, `openReject`, `openSnooze`), so there is exactly
|
|
59
|
+
one place each action's fetch call, refresh and toast live.
|
|
60
|
+
- **Reason and date are dialogs, not confirms.** Reject needs a typed
|
|
61
|
+
reason and snooze needs a date, so both open a small `BbDialog` (the
|
|
62
|
+
dialog itself is the confirmation gate) — a plain `useConfirm` is
|
|
63
|
+
reserved for actions that need no input.
|
|
64
|
+
- **The shortcut bar mirrors `useDeleteCmd`'s guard.** A small keydown
|
|
65
|
+
handler in the context checks `isInteractiveElementFocused()` before
|
|
66
|
+
firing — typing "reason" in the reject textarea never triggers `R`.
|
|
67
|
+
Platform-neutral: identical code to the Vue and Inertia recipes.
|
|
68
|
+
- **Clicking a submitter's name always stacks**, from a row or from inside
|
|
69
|
+
the expense peek — the back arrow appears once the stack is two deep,
|
|
70
|
+
exactly the "peek vs stack" grammar of the flagship.
|
|
71
|
+
- **Tabs are URL filters with live counts** (`Needs approval` / `Approved`
|
|
72
|
+
/ `Rejected` / `All`), same shape as every other workspace in the folder.
|
|
73
|
+
- **Buttons are `md` by default**; `sm`/`xs` apply only where the doctrine
|
|
74
|
+
allows it — table rows, the peek footers, the bulk bar, the shortcut bar.
|
|
75
|
+
|
|
76
|
+
## Page anatomy
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
ApprovalsInbox/
|
|
80
|
+
Index.vue ← assembly: PageShell slots, table, tabs, pagination
|
|
81
|
+
context.ts ← coopting page context (shell + table + URL filters + shortcuts + actions)
|
|
82
|
+
FilterRail.vue ← pill filters + clear-all
|
|
83
|
+
ShortcutsHint.vue ← A / R / S icon buttons, tooltip-labelled, act on the highlighted row
|
|
84
|
+
ReceiptThumbnail.vue ← receipt image/placeholder cell
|
|
85
|
+
RowActions.vue ← per-row approve / reject / snooze icon buttons
|
|
86
|
+
SelectionBar.vue ← bulk approve / reject in the floating bottom panel
|
|
87
|
+
RejectDialog.vue ← single-row reject, required reason
|
|
88
|
+
BulkRejectDialog.vue ← bulk reject, required reason
|
|
89
|
+
SnoozeDialog.vue ← single-row snooze, date picker
|
|
90
|
+
ExpensePeek.vue ← sidebar workbench, stack level 1 (opens from highlight)
|
|
91
|
+
SubmitterPeek.vue ← stacked peek, back arrow via the shell
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Chrome assumed from the guides: [App Layout](../../guides/app-layout.md)
|
|
95
|
+
(left nav + fat footer), [Page Shell](../../guides/page-shell.md),
|
|
96
|
+
[Floating Bottom Panel](../../guides/floating-bottom-panel.md).
|
|
97
|
+
|
|
98
|
+
## Action inventory
|
|
99
|
+
|
|
100
|
+
| # | Action | Trigger | Mechanism |
|
|
101
|
+
| --- | -------------------------------- | ------------------------------------------------------ | --------------------------------------------------------- |
|
|
102
|
+
| 1 | Page search (merchant/submitter) | topbar right | `filters.search` → URL → `useAsyncData` refetch |
|
|
103
|
+
| 2 | Filter by submitter | filter rail pill | `BbSelectPopover` single, query-aware (`$fetch`) |
|
|
104
|
+
| 3 | Filter by category | filter rail pill | `BbSelectPopover` multiple |
|
|
105
|
+
| 4 | Quick filter: amount over | filter rail pill | `BbSelectPopover` single, preset thresholds |
|
|
106
|
+
| 5 | Clear filters | rail right (only when active) | context `clearFilters()` |
|
|
107
|
+
| 6 | Switch tab | href tabs with counts | URL + `useAsyncData` refetch |
|
|
108
|
+
| 7 | Arrow through rows | `keyboard-navigation` roving tabindex | table's own Arrow/Home/End |
|
|
109
|
+
| 8 | Preview an expense | highlight a row (click, Enter/Space, arrow-then-Enter) | `ctx.table.highlighted` watcher → `peekExpense` (replace) |
|
|
110
|
+
| 9 | Approve (one click) | row icon button, peek footer, key `A` | `approve(expense)` — `$fetch` + `refresh()` + toast |
|
|
111
|
+
| 10 | Reject with reason | row icon button, peek footer, key `R` | `openReject` → `RejectDialog` (required `BbTextarea`) |
|
|
112
|
+
| 11 | Snooze | row icon button, peek footer, key `S` | `openSnooze` → `SnoozeDialog` (`BbDatePickerInput`) |
|
|
113
|
+
| 12 | Select rows / select all | table checkboxes | table context |
|
|
114
|
+
| 13 | Bulk approve | floating bottom panel | `bulkApprove()` on `table.selected` |
|
|
115
|
+
| 14 | Bulk reject with reason | floating bottom panel | `BulkRejectDialog`, destructive |
|
|
116
|
+
| 15 | Stack a submitter | submitter cell / peek property | `stackSubmitter(id)` → shell stack, back arrow |
|
|
117
|
+
| 16 | Stack an expense from a profile | recent-expense row inside `SubmitterPeek` | `stackExpense(id)` → shell stack |
|
|
118
|
+
| 17 | View full-size receipt | peek "View receipt" link | navigation (`href`, new tab) |
|
|
119
|
+
| 18 | Open full expense | peek footer | navigation (`to="/expenses/:id"`) |
|
|
120
|
+
| 19 | Back / close peek | shell chrome | shell stack pop / close |
|
|
121
|
+
|
|
122
|
+
Nineteen distinct actions, spanning inline one-click mutation (9),
|
|
123
|
+
input-gated mutation via dialog (10, 11, 14), bulk mutation (13, 14),
|
|
124
|
+
keyboard dispatch (7, 9, 10, 11), navigation (17, 18, 19) and contextual
|
|
125
|
+
drill-down (15, 16) — well past the ≥ 12 bar.
|
|
126
|
+
|
|
127
|
+
## The code
|
|
128
|
+
|
|
129
|
+
### context.ts
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
// ApprovalsInbox/context.ts
|
|
133
|
+
import {
|
|
134
|
+
computed,
|
|
135
|
+
inject,
|
|
136
|
+
provide,
|
|
137
|
+
reactive,
|
|
138
|
+
ref,
|
|
139
|
+
watch,
|
|
140
|
+
type ComputedRef,
|
|
141
|
+
type InjectionKey,
|
|
142
|
+
type Ref,
|
|
143
|
+
} from 'vue';
|
|
144
|
+
import { useMagicKeys, watchDebounced, whenever } from '@vueuse/core';
|
|
145
|
+
import {
|
|
146
|
+
useBbTableContext,
|
|
147
|
+
useConfirm,
|
|
148
|
+
useToast,
|
|
149
|
+
type BbTableContext,
|
|
150
|
+
} from 'bitboss-ui';
|
|
151
|
+
import {
|
|
152
|
+
usePageShellContext,
|
|
153
|
+
type PageShellContext,
|
|
154
|
+
} from '@/components/layout/usePageShellContext';
|
|
155
|
+
import type { Expense, ExpenseCategory } from '@/types';
|
|
156
|
+
|
|
157
|
+
/** What the stacked sidebar can show. Clicking a name pushes one of these. */
|
|
158
|
+
export type PeekItem =
|
|
159
|
+
| { type: 'expense'; id: number }
|
|
160
|
+
| { type: 'submitter'; id: number };
|
|
161
|
+
|
|
162
|
+
export type ApprovalsTab = 'needsApproval' | 'approved' | 'rejected' | 'all';
|
|
163
|
+
|
|
164
|
+
export interface ApprovalsFilters {
|
|
165
|
+
search: string;
|
|
166
|
+
submitterId: number | null;
|
|
167
|
+
categories: ExpenseCategory[];
|
|
168
|
+
/** Preset threshold from the quick filter (`50`, `100`, `250`…), or `null`. */
|
|
169
|
+
amountOver: number | null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
type ExpenseListResponse = {
|
|
173
|
+
data: Expense[];
|
|
174
|
+
total: number;
|
|
175
|
+
tabCounts: Record<ApprovalsTab, number>;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export interface ApprovalsInboxContext {
|
|
179
|
+
// -- Coopted contexts ------------------------------------------------------
|
|
180
|
+
shell: PageShellContext<PeekItem>;
|
|
181
|
+
table: BbTableContext;
|
|
182
|
+
// -- Page-owned state --------------------------------------------------------
|
|
183
|
+
filters: ApprovalsFilters;
|
|
184
|
+
hasActiveFilters: ComputedRef<boolean>;
|
|
185
|
+
tab: Ref<ApprovalsTab>;
|
|
186
|
+
tabCounts: ComputedRef<Record<ApprovalsTab, number>>;
|
|
187
|
+
dialogs: {
|
|
188
|
+
reject: Ref<Expense | null>;
|
|
189
|
+
bulkReject: Ref<boolean>;
|
|
190
|
+
snooze: Ref<Expense | null>;
|
|
191
|
+
};
|
|
192
|
+
// -- Data (server-rendered, URL-driven) ---------------------------------------
|
|
193
|
+
expenses: ComputedRef<Expense[]>;
|
|
194
|
+
expensesPending: Ref<boolean>;
|
|
195
|
+
/** The expense under the roving tabindex, resolved from the last fetch. */
|
|
196
|
+
highlightedExpense: ComputedRef<Expense | null>;
|
|
197
|
+
// -- Peek grammar ----------------------------------------------------------
|
|
198
|
+
// (peekExpense — the highlight-driven replace — is internal to the watcher
|
|
199
|
+
// below, not part of the public surface: leaves never open the peek
|
|
200
|
+
// directly, only highlight drives it.)
|
|
201
|
+
stackExpense: (id: number) => void;
|
|
202
|
+
stackSubmitter: (id: number) => void;
|
|
203
|
+
// -- Actions (complete: fetch + refresh + toast) -----------------------------
|
|
204
|
+
clearFilters: () => void;
|
|
205
|
+
approve: (expense: Expense) => Promise<void>;
|
|
206
|
+
openReject: (expense: Expense) => void;
|
|
207
|
+
reject: (expense: Expense, reason: string) => Promise<void>;
|
|
208
|
+
openSnooze: (expense: Expense) => void;
|
|
209
|
+
snooze: (expense: Expense, until: string) => Promise<void>;
|
|
210
|
+
bulkApprove: () => Promise<void>;
|
|
211
|
+
openBulkReject: () => void;
|
|
212
|
+
bulkReject: (reason: string) => Promise<void>;
|
|
213
|
+
// -- Keyboard-shortcut dispatch (mirrors the row/peek buttons) --------------
|
|
214
|
+
approveHighlighted: () => void;
|
|
215
|
+
rejectHighlighted: () => void;
|
|
216
|
+
snoozeHighlighted: () => void;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const key: InjectionKey<ApprovalsInboxContext> = Symbol('approvals-inbox');
|
|
220
|
+
|
|
221
|
+
/** Mirrors `useDeleteCmd`'s guard: skip shortcuts while the user is typing. */
|
|
222
|
+
const isInteractiveElementFocused = (): boolean => {
|
|
223
|
+
const active = document.activeElement;
|
|
224
|
+
if (!active) return false;
|
|
225
|
+
const tag = active.tagName.toLowerCase();
|
|
226
|
+
if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;
|
|
227
|
+
return active.getAttribute('contenteditable') === 'true';
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/** Comma-joined query value → typed array (or `[]` when absent). */
|
|
231
|
+
const toArray = <T = string>(value: unknown): T[] => {
|
|
232
|
+
if (value == null || value === '') return [];
|
|
233
|
+
const raw = Array.isArray(value) ? value : String(value).split(',');
|
|
234
|
+
return raw as T[];
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export function provideApprovalsInbox(): ApprovalsInboxContext {
|
|
238
|
+
const { toast } = useToast();
|
|
239
|
+
const { confirm } = useConfirm();
|
|
240
|
+
const route = useRoute();
|
|
241
|
+
|
|
242
|
+
// -- Coopt the specific contexts: once, here, for the whole page. ----------
|
|
243
|
+
const shell = usePageShellContext<PeekItem>();
|
|
244
|
+
const table = useBbTableContext('approvals-table', { perPage: 25 });
|
|
245
|
+
|
|
246
|
+
// -- Filters + tab: seeded from the URL, the single source of truth. -------
|
|
247
|
+
const filters = reactive<ApprovalsFilters>({
|
|
248
|
+
search: (route.query.search as string) ?? '',
|
|
249
|
+
submitterId: route.query.submitterId
|
|
250
|
+
? Number(route.query.submitterId)
|
|
251
|
+
: null,
|
|
252
|
+
categories: toArray<ExpenseCategory>(route.query.categories),
|
|
253
|
+
amountOver: route.query.amountOver ? Number(route.query.amountOver) : null,
|
|
254
|
+
});
|
|
255
|
+
const tab = ref<ApprovalsTab>(
|
|
256
|
+
(route.query.tab as ApprovalsTab) ?? 'needsApproval'
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const hasActiveFilters = computed(
|
|
260
|
+
() =>
|
|
261
|
+
filters.search !== '' ||
|
|
262
|
+
filters.submitterId !== null ||
|
|
263
|
+
filters.categories.length > 0 ||
|
|
264
|
+
filters.amountOver !== null
|
|
265
|
+
);
|
|
266
|
+
const clearFilters = () => {
|
|
267
|
+
filters.search = '';
|
|
268
|
+
filters.submitterId = null;
|
|
269
|
+
filters.categories = [];
|
|
270
|
+
filters.amountOver = null;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Every filter/tab change mirrors into the query string; `useAsyncData`
|
|
274
|
+
// below watches `route.query`, so this is the whole refetch trigger.
|
|
275
|
+
const syncQuery = () =>
|
|
276
|
+
navigateTo({
|
|
277
|
+
query: {
|
|
278
|
+
search: filters.search || undefined,
|
|
279
|
+
submitterId: filters.submitterId ?? undefined,
|
|
280
|
+
categories: filters.categories.length
|
|
281
|
+
? filters.categories.join(',')
|
|
282
|
+
: undefined,
|
|
283
|
+
amountOver: filters.amountOver ?? undefined,
|
|
284
|
+
tab: tab.value,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
watchDebounced(filters, syncQuery, { deep: true, debounce: 300 });
|
|
288
|
+
watch(tab, () => {
|
|
289
|
+
table.page.value = 1;
|
|
290
|
+
syncQuery();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// -- Data ---------------------------------------------------------------------
|
|
294
|
+
const { data, pending, refresh } = useAsyncData<ExpenseListResponse>(
|
|
295
|
+
'expenses',
|
|
296
|
+
() =>
|
|
297
|
+
$fetch<ExpenseListResponse>('/api/expenses', {
|
|
298
|
+
query: {
|
|
299
|
+
search: filters.search || undefined,
|
|
300
|
+
submitterId: filters.submitterId ?? undefined,
|
|
301
|
+
categories: filters.categories,
|
|
302
|
+
amountOver: filters.amountOver ?? undefined,
|
|
303
|
+
tab: tab.value,
|
|
304
|
+
page: table.page.value,
|
|
305
|
+
perPage: table.perPage.value,
|
|
306
|
+
},
|
|
307
|
+
}),
|
|
308
|
+
{ watch: [() => route.query, table.page] }
|
|
309
|
+
);
|
|
310
|
+
const expenses = computed(() => data.value?.data ?? []);
|
|
311
|
+
const expensesPending = pending;
|
|
312
|
+
const tabCounts = computed(
|
|
313
|
+
() =>
|
|
314
|
+
data.value?.tabCounts ?? {
|
|
315
|
+
needsApproval: 0,
|
|
316
|
+
approved: 0,
|
|
317
|
+
rejected: 0,
|
|
318
|
+
all: 0,
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
watch(
|
|
322
|
+
data,
|
|
323
|
+
(response) => {
|
|
324
|
+
if (response) table.total.value = response.total;
|
|
325
|
+
},
|
|
326
|
+
{ immediate: true }
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const dialogs = {
|
|
330
|
+
reject: ref<Expense | null>(null),
|
|
331
|
+
bulkReject: ref(false),
|
|
332
|
+
snooze: ref<Expense | null>(null),
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// -- Peek grammar -----------------------------------------------------------------
|
|
336
|
+
// peekX replaces the stack (from the list / highlight); stackX drills down.
|
|
337
|
+
const peekExpense = (id: number) => shell.push({ type: 'expense', id }, true);
|
|
338
|
+
const stackExpense = (id: number) => shell.push({ type: 'expense', id });
|
|
339
|
+
const stackSubmitter = (id: number) => shell.push({ type: 'submitter', id });
|
|
340
|
+
|
|
341
|
+
// Highlighting IS the peek trigger: arrowing (or clicking) a row opens it,
|
|
342
|
+
// clearing the highlight closes it. `item-value="id"` on the table means
|
|
343
|
+
// `table.highlighted` already holds the plain id. This reads the table
|
|
344
|
+
// context, not the URL, so it is unchanged from the Vue recipe.
|
|
345
|
+
watch(table.highlighted, (id) => {
|
|
346
|
+
if (id == null) {
|
|
347
|
+
shell.close();
|
|
348
|
+
} else {
|
|
349
|
+
peekExpense(id as number);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const highlightedExpense = computed<Expense | null>(
|
|
354
|
+
() =>
|
|
355
|
+
expenses.value.find(
|
|
356
|
+
(expense) => expense.id === table.highlighted.value
|
|
357
|
+
) ?? null
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// -- Actions ----------------------------------------------------------------------
|
|
361
|
+
const clearSelection = () => {
|
|
362
|
+
table.selected.value = [];
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const approve = async (expense: Expense) => {
|
|
366
|
+
await $fetch(`/api/expenses/${expense.id}/approve`, { method: 'PATCH' });
|
|
367
|
+
await refresh();
|
|
368
|
+
toast({
|
|
369
|
+
title: `Approved ${expense.merchant} — $${expense.amount.toFixed(2)}.`,
|
|
370
|
+
variant: 'success',
|
|
371
|
+
});
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const openReject = (expense: Expense) => {
|
|
375
|
+
dialogs.reject.value = expense;
|
|
376
|
+
};
|
|
377
|
+
const reject = async (expense: Expense, reason: string) => {
|
|
378
|
+
await $fetch(`/api/expenses/${expense.id}/reject`, {
|
|
379
|
+
method: 'POST',
|
|
380
|
+
body: { reason },
|
|
381
|
+
});
|
|
382
|
+
dialogs.reject.value = null;
|
|
383
|
+
await refresh();
|
|
384
|
+
toast({ title: `Rejected ${expense.merchant}.`, variant: 'success' });
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const openSnooze = (expense: Expense) => {
|
|
388
|
+
dialogs.snooze.value = expense;
|
|
389
|
+
};
|
|
390
|
+
const snooze = async (expense: Expense, until: string) => {
|
|
391
|
+
await $fetch(`/api/expenses/${expense.id}/snooze`, {
|
|
392
|
+
method: 'PATCH',
|
|
393
|
+
body: { until },
|
|
394
|
+
});
|
|
395
|
+
dialogs.snooze.value = null;
|
|
396
|
+
await refresh();
|
|
397
|
+
toast({ title: `${expense.merchant} snoozed.`, variant: 'success' });
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const bulkApprove = async () => {
|
|
401
|
+
const ids = table.selected.value as number[];
|
|
402
|
+
await $fetch('/api/expenses/bulk/approve', {
|
|
403
|
+
method: 'PATCH',
|
|
404
|
+
body: { ids },
|
|
405
|
+
});
|
|
406
|
+
clearSelection();
|
|
407
|
+
await refresh();
|
|
408
|
+
toast({ title: `${ids.length} expenses approved.`, variant: 'success' });
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const openBulkReject = () => {
|
|
412
|
+
dialogs.bulkReject.value = true;
|
|
413
|
+
};
|
|
414
|
+
const bulkReject = async (reason: string) => {
|
|
415
|
+
const ids = table.selected.value as number[];
|
|
416
|
+
const ok = await confirm({
|
|
417
|
+
variant: 'destructive',
|
|
418
|
+
title: 'Reject expenses',
|
|
419
|
+
text: `Reject ${ids.length} expenses? The submitters are notified.`,
|
|
420
|
+
yes: 'Reject',
|
|
421
|
+
no: 'Cancel',
|
|
422
|
+
});
|
|
423
|
+
if (!ok) return;
|
|
424
|
+
await $fetch('/api/expenses/bulk/reject', {
|
|
425
|
+
method: 'POST',
|
|
426
|
+
body: { ids, reason },
|
|
427
|
+
});
|
|
428
|
+
dialogs.bulkReject.value = false;
|
|
429
|
+
clearSelection();
|
|
430
|
+
await refresh();
|
|
431
|
+
toast({ title: `${ids.length} expenses rejected.`, variant: 'success' });
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// -- Keyboard-shortcut dispatch: same actions, resolved against the row
|
|
435
|
+
// under the roving tabindex. Guarded exactly like `useDeleteCmd`.
|
|
436
|
+
const keys = useMagicKeys();
|
|
437
|
+
const withHighlighted = (run: (expense: Expense) => void) => {
|
|
438
|
+
if (isInteractiveElementFocused()) return;
|
|
439
|
+
if (
|
|
440
|
+
dialogs.reject.value ||
|
|
441
|
+
dialogs.bulkReject.value ||
|
|
442
|
+
dialogs.snooze.value
|
|
443
|
+
)
|
|
444
|
+
return;
|
|
445
|
+
const expense = highlightedExpense.value;
|
|
446
|
+
if (expense) run(expense);
|
|
447
|
+
};
|
|
448
|
+
const approveHighlighted = () =>
|
|
449
|
+
withHighlighted((expense) => void approve(expense));
|
|
450
|
+
const rejectHighlighted = () => withHighlighted(openReject);
|
|
451
|
+
const snoozeHighlighted = () => withHighlighted(openSnooze);
|
|
452
|
+
|
|
453
|
+
whenever(keys['a'], approveHighlighted);
|
|
454
|
+
whenever(keys['r'], rejectHighlighted);
|
|
455
|
+
whenever(keys['s'], snoozeHighlighted);
|
|
456
|
+
|
|
457
|
+
const ctx: ApprovalsInboxContext = {
|
|
458
|
+
shell,
|
|
459
|
+
table,
|
|
460
|
+
filters,
|
|
461
|
+
hasActiveFilters,
|
|
462
|
+
tab,
|
|
463
|
+
tabCounts,
|
|
464
|
+
dialogs,
|
|
465
|
+
expenses,
|
|
466
|
+
expensesPending,
|
|
467
|
+
highlightedExpense,
|
|
468
|
+
stackExpense,
|
|
469
|
+
stackSubmitter,
|
|
470
|
+
clearFilters,
|
|
471
|
+
approve,
|
|
472
|
+
openReject,
|
|
473
|
+
reject,
|
|
474
|
+
openSnooze,
|
|
475
|
+
snooze,
|
|
476
|
+
bulkApprove,
|
|
477
|
+
openBulkReject,
|
|
478
|
+
bulkReject,
|
|
479
|
+
approveHighlighted,
|
|
480
|
+
rejectHighlighted,
|
|
481
|
+
snoozeHighlighted,
|
|
482
|
+
};
|
|
483
|
+
provide(key, ctx);
|
|
484
|
+
return ctx;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export function useApprovalsInbox(): ApprovalsInboxContext {
|
|
488
|
+
const ctx = inject(key);
|
|
489
|
+
if (!ctx)
|
|
490
|
+
throw new Error('useApprovalsInbox() called outside ApprovalsInbox');
|
|
491
|
+
return ctx;
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Index.vue — the assembly
|
|
496
|
+
|
|
497
|
+
```vue
|
|
498
|
+
<!-- ApprovalsInbox/Index.vue -->
|
|
499
|
+
<template>
|
|
500
|
+
<PageShell>
|
|
501
|
+
<!-- Topbar: breadcrumbs left, page search right. -->
|
|
502
|
+
<template #topbar>
|
|
503
|
+
<div class="flex items-center justify-between gap-4 border-b px-4 py-2">
|
|
504
|
+
<!-- Breadcrumbs take the row's slack on a stable basis (min-w-0 flex-auto);
|
|
505
|
+
without it their overflow recompute fights the flex layout every frame. -->
|
|
506
|
+
<BbBreadcrumbs
|
|
507
|
+
class="min-w-0 flex-auto"
|
|
508
|
+
:items="[
|
|
509
|
+
{ key: 'dashboard', text: 'Dashboard', to: '/dashboard' },
|
|
510
|
+
{ key: 'finance', text: 'Finance', to: '/finance' },
|
|
511
|
+
{ key: 'approvals', text: 'Approvals' },
|
|
512
|
+
]"
|
|
513
|
+
/>
|
|
514
|
+
<BbTextInput
|
|
515
|
+
v-model="ctx.filters.search"
|
|
516
|
+
class="shrink-0"
|
|
517
|
+
clearable
|
|
518
|
+
compact
|
|
519
|
+
hide-label
|
|
520
|
+
label="Search expenses"
|
|
521
|
+
placeholder="Search merchant or submitter…"
|
|
522
|
+
prepend:icon="lucide:search"
|
|
523
|
+
/>
|
|
524
|
+
</div>
|
|
525
|
+
</template>
|
|
526
|
+
|
|
527
|
+
<div class="flex flex-col gap-3 p-4">
|
|
528
|
+
<FilterRail />
|
|
529
|
+
|
|
530
|
+
<div class="flex items-center justify-between gap-2">
|
|
531
|
+
<!-- Tabs as URL filters: counts come from the server on every fetch. -->
|
|
532
|
+
<BbTabs
|
|
533
|
+
v-model="ctx.tab.value"
|
|
534
|
+
compact
|
|
535
|
+
:items="[
|
|
536
|
+
{
|
|
537
|
+
key: 'needsApproval',
|
|
538
|
+
label: `Needs approval · ${ctx.tabCounts.value.needsApproval}`,
|
|
539
|
+
href: '?tab=needsApproval',
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
key: 'approved',
|
|
543
|
+
label: `Approved · ${ctx.tabCounts.value.approved}`,
|
|
544
|
+
href: '?tab=approved',
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
key: 'rejected',
|
|
548
|
+
label: `Rejected · ${ctx.tabCounts.value.rejected}`,
|
|
549
|
+
href: '?tab=rejected',
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
key: 'all',
|
|
553
|
+
label: `All · ${ctx.tabCounts.value.all}`,
|
|
554
|
+
href: '?tab=all',
|
|
555
|
+
},
|
|
556
|
+
]"
|
|
557
|
+
/>
|
|
558
|
+
<ShortcutsHint />
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
<BbTable
|
|
562
|
+
id="approvals-table"
|
|
563
|
+
actions
|
|
564
|
+
caption="Expense approvals"
|
|
565
|
+
:columns="columns"
|
|
566
|
+
compact
|
|
567
|
+
item-value="id"
|
|
568
|
+
:items="ctx.expenses.value"
|
|
569
|
+
keyboard-navigation
|
|
570
|
+
:loading="ctx.expensesPending.value"
|
|
571
|
+
loading-text="Loading expenses…"
|
|
572
|
+
:row-class="rowClass"
|
|
573
|
+
selectable
|
|
574
|
+
>
|
|
575
|
+
<template #receipt="{ item }">
|
|
576
|
+
<ReceiptThumbnail :expense="item" />
|
|
577
|
+
</template>
|
|
578
|
+
|
|
579
|
+
<template #merchant="{ content }">
|
|
580
|
+
<span class="font-medium">{{ content }}</span>
|
|
581
|
+
</template>
|
|
582
|
+
|
|
583
|
+
<template #submitter_name="{ item }">
|
|
584
|
+
<BbButton
|
|
585
|
+
size="sm"
|
|
586
|
+
variant="ghost"
|
|
587
|
+
@click="() => ctx.stackSubmitter(item.submitter.id)"
|
|
588
|
+
>
|
|
589
|
+
{{ item.submitter.name }}
|
|
590
|
+
</BbButton>
|
|
591
|
+
</template>
|
|
592
|
+
|
|
593
|
+
<template #category="{ item }">
|
|
594
|
+
<BbBadge variant="outline">{{ item.category }}</BbBadge>
|
|
595
|
+
</template>
|
|
596
|
+
|
|
597
|
+
<template #status="{ item }">
|
|
598
|
+
<BbBadge variant="outline">{{ statusLabel(item.status) }}</BbBadge>
|
|
599
|
+
</template>
|
|
600
|
+
|
|
601
|
+
<template #actions="{ item }">
|
|
602
|
+
<RowActions :expense="item" />
|
|
603
|
+
</template>
|
|
604
|
+
|
|
605
|
+
<template #no-data>
|
|
606
|
+
<div class="flex flex-col items-center gap-2 p-6 text-center">
|
|
607
|
+
<p class="text-sm text-gray-500">
|
|
608
|
+
No expenses match
|
|
609
|
+
{{
|
|
610
|
+
ctx.hasActiveFilters.value ? 'the current filters' : 'this tab'
|
|
611
|
+
}}.
|
|
612
|
+
</p>
|
|
613
|
+
<BbButton
|
|
614
|
+
v-if="ctx.hasActiveFilters.value"
|
|
615
|
+
variant="ghost"
|
|
616
|
+
@click="ctx.clearFilters"
|
|
617
|
+
>
|
|
618
|
+
Clear filters
|
|
619
|
+
</BbButton>
|
|
620
|
+
</div>
|
|
621
|
+
</template>
|
|
622
|
+
</BbTable>
|
|
623
|
+
|
|
624
|
+
<BbPagination table-id="approvals-table" />
|
|
625
|
+
</div>
|
|
626
|
+
|
|
627
|
+
<SelectionBar />
|
|
628
|
+
|
|
629
|
+
<!-- Stacked peek sidebar: highlight opens the expense; names drill down. -->
|
|
630
|
+
<template #sidebar>
|
|
631
|
+
<ExpensePeek
|
|
632
|
+
v-if="ctx.shell.current.value?.type === 'expense'"
|
|
633
|
+
:expense-id="ctx.shell.current.value.id"
|
|
634
|
+
/>
|
|
635
|
+
<SubmitterPeek
|
|
636
|
+
v-else-if="ctx.shell.current.value?.type === 'submitter'"
|
|
637
|
+
:submitter-id="ctx.shell.current.value.id"
|
|
638
|
+
/>
|
|
639
|
+
</template>
|
|
640
|
+
|
|
641
|
+
<template #dialogs>
|
|
642
|
+
<RejectDialog />
|
|
643
|
+
<BulkRejectDialog />
|
|
644
|
+
<SnoozeDialog />
|
|
645
|
+
</template>
|
|
646
|
+
</PageShell>
|
|
647
|
+
</template>
|
|
648
|
+
|
|
649
|
+
<script setup lang="ts">
|
|
650
|
+
import {
|
|
651
|
+
BbBadge,
|
|
652
|
+
BbBreadcrumbs,
|
|
653
|
+
BbButton,
|
|
654
|
+
BbPagination,
|
|
655
|
+
BbTable,
|
|
656
|
+
BbTabs,
|
|
657
|
+
BbTextInput,
|
|
658
|
+
} from 'bitboss-ui';
|
|
659
|
+
import PageShell from '@/components/layout/PageShell.vue';
|
|
660
|
+
import { provideApprovalsInbox } from './context';
|
|
661
|
+
import BulkRejectDialog from './BulkRejectDialog.vue';
|
|
662
|
+
import ExpensePeek from './ExpensePeek.vue';
|
|
663
|
+
import FilterRail from './FilterRail.vue';
|
|
664
|
+
import ReceiptThumbnail from './ReceiptThumbnail.vue';
|
|
665
|
+
import RejectDialog from './RejectDialog.vue';
|
|
666
|
+
import RowActions from './RowActions.vue';
|
|
667
|
+
import SelectionBar from './SelectionBar.vue';
|
|
668
|
+
import ShortcutsHint from './ShortcutsHint.vue';
|
|
669
|
+
import SnoozeDialog from './SnoozeDialog.vue';
|
|
670
|
+
import SubmitterPeek from './SubmitterPeek.vue';
|
|
671
|
+
import type { Expense, ExpenseStatus } from '@/types';
|
|
672
|
+
|
|
673
|
+
const ctx = provideApprovalsInbox();
|
|
674
|
+
|
|
675
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
676
|
+
style: 'currency',
|
|
677
|
+
currency: 'USD',
|
|
678
|
+
});
|
|
679
|
+
const shortDate = (value: string) =>
|
|
680
|
+
new Date(value).toLocaleDateString('en-US', {
|
|
681
|
+
month: 'short',
|
|
682
|
+
day: 'numeric',
|
|
683
|
+
});
|
|
684
|
+
const statusLabel = (status: ExpenseStatus) =>
|
|
685
|
+
({
|
|
686
|
+
needsApproval: 'Needs approval',
|
|
687
|
+
approved: 'Approved',
|
|
688
|
+
rejected: 'Rejected',
|
|
689
|
+
snoozed: 'Snoozed',
|
|
690
|
+
})[status];
|
|
691
|
+
|
|
692
|
+
const columns = [
|
|
693
|
+
{ key: 'receipt', label: 'Receipt', width: 48 },
|
|
694
|
+
{ key: 'merchant', label: 'Merchant' },
|
|
695
|
+
{ key: 'submitter.name', label: 'Submitter' },
|
|
696
|
+
{ key: 'category', label: 'Category' },
|
|
697
|
+
{
|
|
698
|
+
key: 'amount',
|
|
699
|
+
label: 'Amount',
|
|
700
|
+
align: 'right' as const,
|
|
701
|
+
formatter: (value: number) => currency.format(value),
|
|
702
|
+
},
|
|
703
|
+
{ key: 'submittedAt', label: 'Submitted', formatter: shortDate },
|
|
704
|
+
{ key: 'status', label: 'Status' },
|
|
705
|
+
];
|
|
706
|
+
|
|
707
|
+
// Highlight ships unstyled — paint it via the public row-class API.
|
|
708
|
+
const rowClass = (item: Expense) =>
|
|
709
|
+
ctx.table.highlighted.value === item.id
|
|
710
|
+
? 'bg-indigo-50 dark:bg-indigo-950/20'
|
|
711
|
+
: undefined;
|
|
712
|
+
</script>
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### FilterRail.vue
|
|
716
|
+
|
|
717
|
+
```vue
|
|
718
|
+
<!-- ApprovalsInbox/FilterRail.vue -->
|
|
719
|
+
<template>
|
|
720
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
721
|
+
<!-- Submitter: single, query-aware search against the API. -->
|
|
722
|
+
<BbSelectPopover
|
|
723
|
+
v-model="ctx.filters.submitterId"
|
|
724
|
+
item-text="name"
|
|
725
|
+
item-value="id"
|
|
726
|
+
:items="searchSubmitters"
|
|
727
|
+
>
|
|
728
|
+
<template #activator="{ props }">
|
|
729
|
+
<BbButton
|
|
730
|
+
v-bind="props"
|
|
731
|
+
append:icon="lucide:chevron-down"
|
|
732
|
+
size="sm"
|
|
733
|
+
variant="outline"
|
|
734
|
+
>
|
|
735
|
+
{{ submitterLabel }}
|
|
736
|
+
</BbButton>
|
|
737
|
+
</template>
|
|
738
|
+
</BbSelectPopover>
|
|
739
|
+
|
|
740
|
+
<!-- Category: multi-select pill. -->
|
|
741
|
+
<BbSelectPopover
|
|
742
|
+
v-model="ctx.filters.categories"
|
|
743
|
+
:items="['travel', 'meals', 'software', 'office', 'client', 'other']"
|
|
744
|
+
multiple
|
|
745
|
+
>
|
|
746
|
+
<template #activator="{ props }">
|
|
747
|
+
<BbButton
|
|
748
|
+
v-bind="props"
|
|
749
|
+
append:icon="lucide:chevron-down"
|
|
750
|
+
size="sm"
|
|
751
|
+
variant="outline"
|
|
752
|
+
>
|
|
753
|
+
Category{{
|
|
754
|
+
ctx.filters.categories.length
|
|
755
|
+
? ` · ${ctx.filters.categories.length}`
|
|
756
|
+
: ''
|
|
757
|
+
}}
|
|
758
|
+
</BbButton>
|
|
759
|
+
</template>
|
|
760
|
+
</BbSelectPopover>
|
|
761
|
+
|
|
762
|
+
<!-- Amount over: a quick-filter pill from preset thresholds. -->
|
|
763
|
+
<BbSelectPopover
|
|
764
|
+
v-model="ctx.filters.amountOver"
|
|
765
|
+
disable-writing
|
|
766
|
+
:items="[50, 100, 250, 500, 1000]"
|
|
767
|
+
width="160px"
|
|
768
|
+
>
|
|
769
|
+
<template #activator="{ props }">
|
|
770
|
+
<BbButton
|
|
771
|
+
v-bind="props"
|
|
772
|
+
append:icon="lucide:chevron-down"
|
|
773
|
+
size="sm"
|
|
774
|
+
variant="outline"
|
|
775
|
+
>
|
|
776
|
+
{{
|
|
777
|
+
ctx.filters.amountOver
|
|
778
|
+
? `Over $${ctx.filters.amountOver}`
|
|
779
|
+
: 'Amount'
|
|
780
|
+
}}
|
|
781
|
+
</BbButton>
|
|
782
|
+
</template>
|
|
783
|
+
</BbSelectPopover>
|
|
784
|
+
|
|
785
|
+
<!-- Clear-all renders only while something is active. -->
|
|
786
|
+
<BbButton
|
|
787
|
+
v-if="ctx.hasActiveFilters.value"
|
|
788
|
+
class="ml-auto"
|
|
789
|
+
prepend:icon="lucide:x"
|
|
790
|
+
size="sm"
|
|
791
|
+
variant="ghost"
|
|
792
|
+
@click="ctx.clearFilters"
|
|
793
|
+
>
|
|
794
|
+
Clear filters
|
|
795
|
+
</BbButton>
|
|
796
|
+
</div>
|
|
797
|
+
</template>
|
|
798
|
+
|
|
799
|
+
<script setup lang="ts">
|
|
800
|
+
import { computed, ref, watchEffect } from 'vue';
|
|
801
|
+
import { BbButton, BbSelectPopover } from 'bitboss-ui';
|
|
802
|
+
import { useApprovalsInbox } from './context';
|
|
803
|
+
import type { Submitter } from '@/types';
|
|
804
|
+
|
|
805
|
+
const ctx = useApprovalsInbox();
|
|
806
|
+
|
|
807
|
+
// Query-aware items function: BbSelectPopover calls it with the search text.
|
|
808
|
+
const searchSubmitters = (query: string): Promise<Submitter[]> =>
|
|
809
|
+
$fetch<Submitter[]>('/api/submitters', { query: { search: query } });
|
|
810
|
+
|
|
811
|
+
// The trigger labels itself from page state (a standalone popover with a
|
|
812
|
+
// seeded model stays lazy and has no resolvable `text` before first open).
|
|
813
|
+
const selectedSubmitter = ref<Submitter | null>(null);
|
|
814
|
+
watchEffect(async () => {
|
|
815
|
+
selectedSubmitter.value =
|
|
816
|
+
ctx.filters.submitterId == null
|
|
817
|
+
? null
|
|
818
|
+
: await $fetch<Submitter>(`/api/submitters/${ctx.filters.submitterId}`);
|
|
819
|
+
});
|
|
820
|
+
const submitterLabel = computed(
|
|
821
|
+
() => selectedSubmitter.value?.name ?? 'Submitter'
|
|
822
|
+
);
|
|
823
|
+
</script>
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### ShortcutsHint.vue
|
|
827
|
+
|
|
828
|
+
```vue
|
|
829
|
+
<!-- ApprovalsInbox/ShortcutsHint.vue -->
|
|
830
|
+
<template>
|
|
831
|
+
<div class="flex items-center gap-1">
|
|
832
|
+
<span class="mr-1 text-xs text-gray-500">Shortcuts</span>
|
|
833
|
+
<BbTooltip>
|
|
834
|
+
<template #activator="{ props }">
|
|
835
|
+
<BbButton
|
|
836
|
+
v-bind="props"
|
|
837
|
+
aria-label="Approve highlighted expense"
|
|
838
|
+
:disabled="!ctx.highlightedExpense.value"
|
|
839
|
+
icon="lucide:check"
|
|
840
|
+
size="xs"
|
|
841
|
+
variant="ghost"
|
|
842
|
+
@click="ctx.approveHighlighted"
|
|
843
|
+
/>
|
|
844
|
+
</template>
|
|
845
|
+
<span class="flex items-center gap-1.5">
|
|
846
|
+
Approve
|
|
847
|
+
<kbd
|
|
848
|
+
class="rounded border border-current px-1 font-mono text-[10px] leading-4 opacity-75"
|
|
849
|
+
>A</kbd
|
|
850
|
+
>
|
|
851
|
+
</span>
|
|
852
|
+
</BbTooltip>
|
|
853
|
+
<BbTooltip>
|
|
854
|
+
<template #activator="{ props }">
|
|
855
|
+
<BbButton
|
|
856
|
+
v-bind="props"
|
|
857
|
+
aria-label="Reject highlighted expense"
|
|
858
|
+
:disabled="!ctx.highlightedExpense.value"
|
|
859
|
+
icon="lucide:x"
|
|
860
|
+
size="xs"
|
|
861
|
+
variant="ghost"
|
|
862
|
+
@click="ctx.rejectHighlighted"
|
|
863
|
+
/>
|
|
864
|
+
</template>
|
|
865
|
+
<span class="flex items-center gap-1.5">
|
|
866
|
+
Reject
|
|
867
|
+
<kbd
|
|
868
|
+
class="rounded border border-current px-1 font-mono text-[10px] leading-4 opacity-75"
|
|
869
|
+
>R</kbd
|
|
870
|
+
>
|
|
871
|
+
</span>
|
|
872
|
+
</BbTooltip>
|
|
873
|
+
<BbTooltip>
|
|
874
|
+
<template #activator="{ props }">
|
|
875
|
+
<BbButton
|
|
876
|
+
v-bind="props"
|
|
877
|
+
aria-label="Snooze highlighted expense"
|
|
878
|
+
:disabled="!ctx.highlightedExpense.value"
|
|
879
|
+
icon="lucide:clock"
|
|
880
|
+
size="xs"
|
|
881
|
+
variant="ghost"
|
|
882
|
+
@click="ctx.snoozeHighlighted"
|
|
883
|
+
/>
|
|
884
|
+
</template>
|
|
885
|
+
<span class="flex items-center gap-1.5">
|
|
886
|
+
Snooze
|
|
887
|
+
<kbd
|
|
888
|
+
class="rounded border border-current px-1 font-mono text-[10px] leading-4 opacity-75"
|
|
889
|
+
>S</kbd
|
|
890
|
+
>
|
|
891
|
+
</span>
|
|
892
|
+
</BbTooltip>
|
|
893
|
+
</div>
|
|
894
|
+
</template>
|
|
895
|
+
|
|
896
|
+
<script setup lang="ts">
|
|
897
|
+
import { BbButton, BbTooltip } from 'bitboss-ui';
|
|
898
|
+
import { useApprovalsInbox } from './context';
|
|
899
|
+
|
|
900
|
+
const ctx = useApprovalsInbox();
|
|
901
|
+
</script>
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
### ReceiptThumbnail.vue
|
|
905
|
+
|
|
906
|
+
```vue
|
|
907
|
+
<!-- ApprovalsInbox/ReceiptThumbnail.vue -->
|
|
908
|
+
<template>
|
|
909
|
+
<img
|
|
910
|
+
v-if="expense.receiptUrl"
|
|
911
|
+
:alt="`Receipt from ${expense.merchant}`"
|
|
912
|
+
class="h-8 w-8 rounded-[var(--bb-radius)] border border-gray-200 object-cover dark:border-gray-700"
|
|
913
|
+
:src="expense.receiptUrl"
|
|
914
|
+
/>
|
|
915
|
+
<div
|
|
916
|
+
v-else
|
|
917
|
+
aria-hidden="true"
|
|
918
|
+
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"
|
|
919
|
+
>
|
|
920
|
+
<BbIcon icon="lucide:receipt" size="14" />
|
|
921
|
+
</div>
|
|
922
|
+
</template>
|
|
923
|
+
|
|
924
|
+
<script setup lang="ts">
|
|
925
|
+
import { BbIcon } from 'bitboss-ui';
|
|
926
|
+
import type { Expense } from '@/types';
|
|
927
|
+
|
|
928
|
+
defineProps<{ expense: Expense }>();
|
|
929
|
+
</script>
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
### RowActions.vue
|
|
933
|
+
|
|
934
|
+
```vue
|
|
935
|
+
<!-- ApprovalsInbox/RowActions.vue -->
|
|
936
|
+
<template>
|
|
937
|
+
<div class="flex items-center gap-1">
|
|
938
|
+
<BbTooltip text="Approve">
|
|
939
|
+
<template #activator="{ props }">
|
|
940
|
+
<BbButton
|
|
941
|
+
v-bind="props"
|
|
942
|
+
aria-label="Approve"
|
|
943
|
+
:disabled="resolved"
|
|
944
|
+
icon="lucide:check"
|
|
945
|
+
size="xs"
|
|
946
|
+
variant="ghost"
|
|
947
|
+
@click="() => ctx.approve(expense)"
|
|
948
|
+
/>
|
|
949
|
+
</template>
|
|
950
|
+
</BbTooltip>
|
|
951
|
+
<BbTooltip text="Reject">
|
|
952
|
+
<template #activator="{ props }">
|
|
953
|
+
<BbButton
|
|
954
|
+
v-bind="props"
|
|
955
|
+
aria-label="Reject"
|
|
956
|
+
:disabled="resolved"
|
|
957
|
+
icon="lucide:x"
|
|
958
|
+
size="xs"
|
|
959
|
+
variant="ghost"
|
|
960
|
+
@click="() => ctx.openReject(expense)"
|
|
961
|
+
/>
|
|
962
|
+
</template>
|
|
963
|
+
</BbTooltip>
|
|
964
|
+
<BbTooltip text="Snooze">
|
|
965
|
+
<template #activator="{ props }">
|
|
966
|
+
<BbButton
|
|
967
|
+
v-bind="props"
|
|
968
|
+
aria-label="Snooze"
|
|
969
|
+
:disabled="resolved"
|
|
970
|
+
icon="lucide:clock"
|
|
971
|
+
size="xs"
|
|
972
|
+
variant="ghost"
|
|
973
|
+
@click="() => ctx.openSnooze(expense)"
|
|
974
|
+
/>
|
|
975
|
+
</template>
|
|
976
|
+
</BbTooltip>
|
|
977
|
+
</div>
|
|
978
|
+
</template>
|
|
979
|
+
|
|
980
|
+
<script setup lang="ts">
|
|
981
|
+
import { computed } from 'vue';
|
|
982
|
+
import { BbButton, BbTooltip } from 'bitboss-ui';
|
|
983
|
+
import { useApprovalsInbox } from './context';
|
|
984
|
+
import type { Expense } from '@/types';
|
|
985
|
+
|
|
986
|
+
const props = defineProps<{ expense: Expense }>();
|
|
987
|
+
|
|
988
|
+
const ctx = useApprovalsInbox();
|
|
989
|
+
|
|
990
|
+
const resolved = computed(
|
|
991
|
+
() =>
|
|
992
|
+
props.expense.status === 'approved' || props.expense.status === 'rejected'
|
|
993
|
+
);
|
|
994
|
+
</script>
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
### RejectDialog.vue
|
|
998
|
+
|
|
999
|
+
```vue
|
|
1000
|
+
<!-- ApprovalsInbox/RejectDialog.vue -->
|
|
1001
|
+
<template>
|
|
1002
|
+
<BbDialog
|
|
1003
|
+
:model-value="expense !== null"
|
|
1004
|
+
size="sm"
|
|
1005
|
+
:title="expense ? `Reject — ${expense.merchant}` : ''"
|
|
1006
|
+
@show="reason = ''"
|
|
1007
|
+
@update:model-value="close"
|
|
1008
|
+
>
|
|
1009
|
+
<form
|
|
1010
|
+
v-if="expense"
|
|
1011
|
+
class="flex flex-col gap-3"
|
|
1012
|
+
novalidate
|
|
1013
|
+
@submit.prevent="submit"
|
|
1014
|
+
>
|
|
1015
|
+
<BbTextarea
|
|
1016
|
+
v-model="reason"
|
|
1017
|
+
label="Reason"
|
|
1018
|
+
label-mode="outside"
|
|
1019
|
+
placeholder="Why is this expense being rejected?"
|
|
1020
|
+
rows="3"
|
|
1021
|
+
/>
|
|
1022
|
+
<div class="mt-1 flex justify-end gap-2">
|
|
1023
|
+
<BbButton type="button" variant="ghost" @click="close">Cancel</BbButton>
|
|
1024
|
+
<BbButton
|
|
1025
|
+
:disabled="reason.trim().length < 3"
|
|
1026
|
+
type="submit"
|
|
1027
|
+
variant="destructive"
|
|
1028
|
+
>
|
|
1029
|
+
Reject expense
|
|
1030
|
+
</BbButton>
|
|
1031
|
+
</div>
|
|
1032
|
+
</form>
|
|
1033
|
+
</BbDialog>
|
|
1034
|
+
</template>
|
|
1035
|
+
|
|
1036
|
+
<script setup lang="ts">
|
|
1037
|
+
import { computed, ref } from 'vue';
|
|
1038
|
+
import { BbButton, BbDialog, BbTextarea } from 'bitboss-ui';
|
|
1039
|
+
import { useApprovalsInbox } from './context';
|
|
1040
|
+
|
|
1041
|
+
const ctx = useApprovalsInbox();
|
|
1042
|
+
|
|
1043
|
+
// The dialog's subject lives in the page context — the row action and the
|
|
1044
|
+
// peek's reject button both just assign it.
|
|
1045
|
+
const expense = computed(() => ctx.dialogs.reject.value);
|
|
1046
|
+
const close = () => {
|
|
1047
|
+
ctx.dialogs.reject.value = null;
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
const reason = ref('');
|
|
1051
|
+
const submit = () =>
|
|
1052
|
+
expense.value ? ctx.reject(expense.value, reason.value.trim()) : undefined;
|
|
1053
|
+
</script>
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
### BulkRejectDialog.vue
|
|
1057
|
+
|
|
1058
|
+
```vue
|
|
1059
|
+
<!-- ApprovalsInbox/BulkRejectDialog.vue -->
|
|
1060
|
+
<template>
|
|
1061
|
+
<BbDialog
|
|
1062
|
+
:model-value="ctx.dialogs.bulkReject.value"
|
|
1063
|
+
size="sm"
|
|
1064
|
+
:title="`Reject ${count} expenses`"
|
|
1065
|
+
@show="reason = ''"
|
|
1066
|
+
@update:model-value="close"
|
|
1067
|
+
>
|
|
1068
|
+
<form class="flex flex-col gap-3" novalidate @submit.prevent="submit">
|
|
1069
|
+
<BbTextarea
|
|
1070
|
+
v-model="reason"
|
|
1071
|
+
label="Reason"
|
|
1072
|
+
label-mode="outside"
|
|
1073
|
+
placeholder="Applied to every selected expense."
|
|
1074
|
+
rows="3"
|
|
1075
|
+
/>
|
|
1076
|
+
<div class="mt-1 flex justify-end gap-2">
|
|
1077
|
+
<BbButton type="button" variant="ghost" @click="close">Cancel</BbButton>
|
|
1078
|
+
<BbButton
|
|
1079
|
+
:disabled="reason.trim().length < 3"
|
|
1080
|
+
type="submit"
|
|
1081
|
+
variant="destructive"
|
|
1082
|
+
>
|
|
1083
|
+
Reject {{ count }} expenses
|
|
1084
|
+
</BbButton>
|
|
1085
|
+
</div>
|
|
1086
|
+
</form>
|
|
1087
|
+
</BbDialog>
|
|
1088
|
+
</template>
|
|
1089
|
+
|
|
1090
|
+
<script setup lang="ts">
|
|
1091
|
+
import { computed, ref } from 'vue';
|
|
1092
|
+
import { BbButton, BbDialog, BbTextarea } from 'bitboss-ui';
|
|
1093
|
+
import { useApprovalsInbox } from './context';
|
|
1094
|
+
|
|
1095
|
+
const ctx = useApprovalsInbox();
|
|
1096
|
+
|
|
1097
|
+
const count = computed(() => (ctx.table.selected.value as number[]).length);
|
|
1098
|
+
const close = () => {
|
|
1099
|
+
ctx.dialogs.bulkReject.value = false;
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
const reason = ref('');
|
|
1103
|
+
const submit = () => ctx.bulkReject(reason.value.trim());
|
|
1104
|
+
</script>
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
### SnoozeDialog.vue
|
|
1108
|
+
|
|
1109
|
+
```vue
|
|
1110
|
+
<!-- ApprovalsInbox/SnoozeDialog.vue -->
|
|
1111
|
+
<template>
|
|
1112
|
+
<BbDialog
|
|
1113
|
+
:model-value="expense !== null"
|
|
1114
|
+
size="xs"
|
|
1115
|
+
:title="expense ? `Snooze — ${expense.merchant}` : ''"
|
|
1116
|
+
@show="until = tomorrow"
|
|
1117
|
+
@update:model-value="close"
|
|
1118
|
+
>
|
|
1119
|
+
<form
|
|
1120
|
+
v-if="expense"
|
|
1121
|
+
class="flex flex-col gap-3"
|
|
1122
|
+
novalidate
|
|
1123
|
+
@submit.prevent="submit"
|
|
1124
|
+
>
|
|
1125
|
+
<BbDatePickerInput
|
|
1126
|
+
v-model="until"
|
|
1127
|
+
floating
|
|
1128
|
+
label="Snooze until"
|
|
1129
|
+
label-mode="outside"
|
|
1130
|
+
:min="tomorrow"
|
|
1131
|
+
/>
|
|
1132
|
+
<div class="mt-1 flex justify-end gap-2">
|
|
1133
|
+
<BbButton type="button" variant="ghost" @click="close">Cancel</BbButton>
|
|
1134
|
+
<BbButton type="submit" variant="secondary">Snooze</BbButton>
|
|
1135
|
+
</div>
|
|
1136
|
+
</form>
|
|
1137
|
+
</BbDialog>
|
|
1138
|
+
</template>
|
|
1139
|
+
|
|
1140
|
+
<script setup lang="ts">
|
|
1141
|
+
import { computed, ref } from 'vue';
|
|
1142
|
+
import { BbButton, BbDatePickerInput, BbDialog } from 'bitboss-ui';
|
|
1143
|
+
import { useApprovalsInbox } from './context';
|
|
1144
|
+
|
|
1145
|
+
const ctx = useApprovalsInbox();
|
|
1146
|
+
|
|
1147
|
+
const expense = computed(() => ctx.dialogs.snooze.value);
|
|
1148
|
+
const close = () => {
|
|
1149
|
+
ctx.dialogs.snooze.value = null;
|
|
1150
|
+
};
|
|
1151
|
+
|
|
1152
|
+
const tomorrow = new Date(Date.now() + 86_400_000).toISOString().slice(0, 10);
|
|
1153
|
+
const until = ref(tomorrow);
|
|
1154
|
+
const submit = () =>
|
|
1155
|
+
expense.value ? ctx.snooze(expense.value, until.value) : undefined;
|
|
1156
|
+
</script>
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
### SelectionBar.vue
|
|
1160
|
+
|
|
1161
|
+
```vue
|
|
1162
|
+
<!-- ApprovalsInbox/SelectionBar.vue -->
|
|
1163
|
+
<template>
|
|
1164
|
+
<FloatingBottomPanel :open="selectedCount > 0">
|
|
1165
|
+
<span class="px-2 text-sm">{{ selectedCount }} selected</span>
|
|
1166
|
+
<BbButton
|
|
1167
|
+
prepend:icon="lucide:check"
|
|
1168
|
+
size="sm"
|
|
1169
|
+
variant="ghost"
|
|
1170
|
+
@click="ctx.bulkApprove"
|
|
1171
|
+
>
|
|
1172
|
+
Approve
|
|
1173
|
+
</BbButton>
|
|
1174
|
+
<div class="floating-bottom-panel__right flex items-center gap-1">
|
|
1175
|
+
<BbButton size="sm" variant="destructive" @click="ctx.openBulkReject">
|
|
1176
|
+
Reject
|
|
1177
|
+
</BbButton>
|
|
1178
|
+
<BbButton
|
|
1179
|
+
aria-label="Clear selection"
|
|
1180
|
+
icon="lucide:x"
|
|
1181
|
+
size="sm"
|
|
1182
|
+
variant="ghost"
|
|
1183
|
+
@click="clearSelection"
|
|
1184
|
+
/>
|
|
1185
|
+
</div>
|
|
1186
|
+
</FloatingBottomPanel>
|
|
1187
|
+
</template>
|
|
1188
|
+
|
|
1189
|
+
<script setup lang="ts">
|
|
1190
|
+
import { computed } from 'vue';
|
|
1191
|
+
import { BbButton } from 'bitboss-ui';
|
|
1192
|
+
import FloatingBottomPanel from '@/components/layout/FloatingBottomPanel.vue';
|
|
1193
|
+
import { useApprovalsInbox } from './context';
|
|
1194
|
+
|
|
1195
|
+
const ctx = useApprovalsInbox();
|
|
1196
|
+
|
|
1197
|
+
const selectedCount = computed(() => ctx.table.selected.value?.length ?? 0);
|
|
1198
|
+
const clearSelection = () => {
|
|
1199
|
+
ctx.table.selected.value = [];
|
|
1200
|
+
};
|
|
1201
|
+
</script>
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
### ExpensePeek.vue
|
|
1205
|
+
|
|
1206
|
+
```vue
|
|
1207
|
+
<!-- ApprovalsInbox/ExpensePeek.vue -->
|
|
1208
|
+
<template>
|
|
1209
|
+
<div v-if="expense" class="flex flex-col gap-4 text-sm">
|
|
1210
|
+
<!-- Header: identity + amount + status. -->
|
|
1211
|
+
<header class="flex flex-col gap-1.5">
|
|
1212
|
+
<div class="flex items-center justify-between gap-2">
|
|
1213
|
+
<h2 class="text-base font-semibold">{{ expense.merchant }}</h2>
|
|
1214
|
+
<BbBadge variant="outline">{{ statusLabel(expense.status) }}</BbBadge>
|
|
1215
|
+
</div>
|
|
1216
|
+
<p class="text-xl font-semibold">{{ currency.format(expense.amount) }}</p>
|
|
1217
|
+
<p class="text-xs text-gray-500">
|
|
1218
|
+
Submitted {{ longDate(expense.submittedAt) }}
|
|
1219
|
+
</p>
|
|
1220
|
+
</header>
|
|
1221
|
+
|
|
1222
|
+
<!-- Receipt: bigger than the row thumbnail, with a full-size link out. -->
|
|
1223
|
+
<section class="flex flex-col gap-1.5">
|
|
1224
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1225
|
+
Receipt
|
|
1226
|
+
</h3>
|
|
1227
|
+
<img
|
|
1228
|
+
v-if="expense.receiptUrl"
|
|
1229
|
+
:alt="`Receipt from ${expense.merchant}`"
|
|
1230
|
+
class="h-40 w-full rounded-[var(--bb-radius)] border border-gray-200 object-cover dark:border-gray-700"
|
|
1231
|
+
:src="expense.receiptUrl"
|
|
1232
|
+
/>
|
|
1233
|
+
<p v-else class="text-xs text-gray-500">No receipt attached.</p>
|
|
1234
|
+
<BbButton
|
|
1235
|
+
v-if="expense.receiptUrl"
|
|
1236
|
+
append:icon="lucide:arrow-up-right"
|
|
1237
|
+
:href="expense.receiptUrl"
|
|
1238
|
+
size="sm"
|
|
1239
|
+
target="_blank"
|
|
1240
|
+
variant="ghost"
|
|
1241
|
+
>
|
|
1242
|
+
View full-size receipt
|
|
1243
|
+
</BbButton>
|
|
1244
|
+
</section>
|
|
1245
|
+
|
|
1246
|
+
<!-- Properties: submitter name drills DOWN — stacked peek, back arrow. -->
|
|
1247
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5">
|
|
1248
|
+
<dt class="text-gray-500">Submitter</dt>
|
|
1249
|
+
<dd>
|
|
1250
|
+
<BbButton
|
|
1251
|
+
size="sm"
|
|
1252
|
+
variant="link"
|
|
1253
|
+
@click="() => ctx.stackSubmitter(expense!.submitter.id)"
|
|
1254
|
+
>
|
|
1255
|
+
{{ expense.submitter.name }}
|
|
1256
|
+
</BbButton>
|
|
1257
|
+
</dd>
|
|
1258
|
+
<dt class="text-gray-500">Category</dt>
|
|
1259
|
+
<dd class="capitalize">{{ expense.category }}</dd>
|
|
1260
|
+
<dt class="text-gray-500">Memo</dt>
|
|
1261
|
+
<dd>{{ expense.memo ?? '—' }}</dd>
|
|
1262
|
+
</dl>
|
|
1263
|
+
|
|
1264
|
+
<!-- Activity feed. -->
|
|
1265
|
+
<section class="flex flex-col gap-1.5">
|
|
1266
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1267
|
+
Activity
|
|
1268
|
+
</h3>
|
|
1269
|
+
<ul class="flex flex-col gap-2">
|
|
1270
|
+
<li
|
|
1271
|
+
v-for="entry in expense.activity"
|
|
1272
|
+
:key="entry.id"
|
|
1273
|
+
class="flex gap-2"
|
|
1274
|
+
>
|
|
1275
|
+
<BbIcon
|
|
1276
|
+
class="mt-0.5 shrink-0 text-gray-400"
|
|
1277
|
+
:icon="entry.icon"
|
|
1278
|
+
size="14"
|
|
1279
|
+
/>
|
|
1280
|
+
<div class="flex flex-col">
|
|
1281
|
+
<span>{{ entry.text }}</span>
|
|
1282
|
+
<span class="text-xs text-gray-500"
|
|
1283
|
+
>{{ entry.actor }} · {{ longDate(entry.at) }}</span
|
|
1284
|
+
>
|
|
1285
|
+
</div>
|
|
1286
|
+
</li>
|
|
1287
|
+
</ul>
|
|
1288
|
+
</section>
|
|
1289
|
+
|
|
1290
|
+
<!-- Peek is a workbench: the same three actions as the row, plus the real page. -->
|
|
1291
|
+
<footer class="mt-2 flex flex-wrap items-center gap-2 border-t pt-3">
|
|
1292
|
+
<BbButton
|
|
1293
|
+
:disabled="resolved"
|
|
1294
|
+
prepend:icon="lucide:check"
|
|
1295
|
+
size="sm"
|
|
1296
|
+
variant="secondary"
|
|
1297
|
+
@click="() => ctx.approve(expense!)"
|
|
1298
|
+
>
|
|
1299
|
+
Approve
|
|
1300
|
+
</BbButton>
|
|
1301
|
+
<BbButton
|
|
1302
|
+
:disabled="resolved"
|
|
1303
|
+
prepend:icon="lucide:x"
|
|
1304
|
+
size="sm"
|
|
1305
|
+
variant="destructive"
|
|
1306
|
+
@click="() => ctx.openReject(expense!)"
|
|
1307
|
+
>
|
|
1308
|
+
Reject
|
|
1309
|
+
</BbButton>
|
|
1310
|
+
<BbButton
|
|
1311
|
+
:disabled="resolved"
|
|
1312
|
+
prepend:icon="lucide:clock"
|
|
1313
|
+
size="sm"
|
|
1314
|
+
variant="outline"
|
|
1315
|
+
@click="() => ctx.openSnooze(expense!)"
|
|
1316
|
+
>
|
|
1317
|
+
Snooze
|
|
1318
|
+
</BbButton>
|
|
1319
|
+
<BbButton
|
|
1320
|
+
append:icon="lucide:arrow-up-right"
|
|
1321
|
+
class="ml-auto"
|
|
1322
|
+
size="sm"
|
|
1323
|
+
:to="`/expenses/${expense.id}`"
|
|
1324
|
+
variant="ghost"
|
|
1325
|
+
>
|
|
1326
|
+
Open full expense
|
|
1327
|
+
</BbButton>
|
|
1328
|
+
</footer>
|
|
1329
|
+
</div>
|
|
1330
|
+
|
|
1331
|
+
<div v-else class="flex justify-center p-8">
|
|
1332
|
+
<BbSpinner />
|
|
1333
|
+
</div>
|
|
1334
|
+
</template>
|
|
1335
|
+
|
|
1336
|
+
<script setup lang="ts">
|
|
1337
|
+
import { computed, ref, watchEffect } from 'vue';
|
|
1338
|
+
import { BbBadge, BbButton, BbIcon, BbSpinner } from 'bitboss-ui';
|
|
1339
|
+
import { useApprovalsInbox } from './context';
|
|
1340
|
+
import type { ExpenseDetail, ExpenseStatus } from '@/types';
|
|
1341
|
+
|
|
1342
|
+
const props = defineProps<{ expenseId: number }>();
|
|
1343
|
+
|
|
1344
|
+
const ctx = useApprovalsInbox();
|
|
1345
|
+
|
|
1346
|
+
// Re-fetches whenever the stack shows a different expense; re-runs whenever
|
|
1347
|
+
// the list refreshes too — `approve`/`reject`/`snooze` all call `refresh()`,
|
|
1348
|
+
// which updates `ctx.expenses` and re-triggers this watcher.
|
|
1349
|
+
const expense = ref<ExpenseDetail | null>(null);
|
|
1350
|
+
watchEffect(async () => {
|
|
1351
|
+
void ctx.expenses.value;
|
|
1352
|
+
expense.value = await $fetch<ExpenseDetail>(
|
|
1353
|
+
`/api/expenses/${props.expenseId}`
|
|
1354
|
+
);
|
|
1355
|
+
});
|
|
1356
|
+
|
|
1357
|
+
const resolved = computed(
|
|
1358
|
+
() =>
|
|
1359
|
+
expense.value?.status === 'approved' || expense.value?.status === 'rejected'
|
|
1360
|
+
);
|
|
1361
|
+
|
|
1362
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1363
|
+
style: 'currency',
|
|
1364
|
+
currency: 'USD',
|
|
1365
|
+
});
|
|
1366
|
+
const longDate = (value: string) =>
|
|
1367
|
+
new Date(value).toLocaleDateString('en-US', {
|
|
1368
|
+
month: 'short',
|
|
1369
|
+
day: 'numeric',
|
|
1370
|
+
year: 'numeric',
|
|
1371
|
+
});
|
|
1372
|
+
const statusLabel = (status: ExpenseStatus) =>
|
|
1373
|
+
({
|
|
1374
|
+
needsApproval: 'Needs approval',
|
|
1375
|
+
approved: 'Approved',
|
|
1376
|
+
rejected: 'Rejected',
|
|
1377
|
+
snoozed: 'Snoozed',
|
|
1378
|
+
})[status];
|
|
1379
|
+
</script>
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
### SubmitterPeek.vue
|
|
1383
|
+
|
|
1384
|
+
```vue
|
|
1385
|
+
<!-- ApprovalsInbox/SubmitterPeek.vue -->
|
|
1386
|
+
<template>
|
|
1387
|
+
<div v-if="submitter" class="flex flex-col gap-4 text-sm">
|
|
1388
|
+
<header class="flex items-center gap-3">
|
|
1389
|
+
<BbAvatar :alt="submitter.name" :src="submitter.avatarUrl" />
|
|
1390
|
+
<div class="flex flex-col">
|
|
1391
|
+
<h2 class="text-base font-semibold">{{ submitter.name }}</h2>
|
|
1392
|
+
<p class="text-xs text-gray-500">
|
|
1393
|
+
{{ submitter.email }} · {{ submitter.team }}
|
|
1394
|
+
</p>
|
|
1395
|
+
</div>
|
|
1396
|
+
</header>
|
|
1397
|
+
|
|
1398
|
+
<!-- Stat strip. -->
|
|
1399
|
+
<div class="grid grid-cols-3 gap-2">
|
|
1400
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1401
|
+
<p class="text-xs text-gray-500">Submitted (YTD)</p>
|
|
1402
|
+
<p class="font-semibold">
|
|
1403
|
+
{{ currency.format(submitter.totalSubmitted) }}
|
|
1404
|
+
</p>
|
|
1405
|
+
</div>
|
|
1406
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1407
|
+
<p class="text-xs text-gray-500">Pending</p>
|
|
1408
|
+
<p class="font-semibold">{{ submitter.pendingCount }}</p>
|
|
1409
|
+
</div>
|
|
1410
|
+
<div class="rounded-[var(--bb-radius)] border p-2">
|
|
1411
|
+
<p class="text-xs text-gray-500">Approval rate</p>
|
|
1412
|
+
<p class="font-semibold">{{ submitter.approvalRate }}%</p>
|
|
1413
|
+
</div>
|
|
1414
|
+
</div>
|
|
1415
|
+
|
|
1416
|
+
<!-- Recent expenses: names drill down again — the stack keeps growing. -->
|
|
1417
|
+
<section class="flex flex-col gap-1.5">
|
|
1418
|
+
<h3 class="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
1419
|
+
Recent expenses
|
|
1420
|
+
</h3>
|
|
1421
|
+
<ul class="flex flex-col">
|
|
1422
|
+
<li
|
|
1423
|
+
v-for="recent in submitter.recentExpenses"
|
|
1424
|
+
:key="recent.id"
|
|
1425
|
+
class="flex items-center justify-between gap-2 border-b py-1.5 last:border-b-0"
|
|
1426
|
+
>
|
|
1427
|
+
<BbButton
|
|
1428
|
+
size="sm"
|
|
1429
|
+
variant="link"
|
|
1430
|
+
@click="() => ctx.stackExpense(recent.id)"
|
|
1431
|
+
>
|
|
1432
|
+
{{ recent.merchant }}
|
|
1433
|
+
</BbButton>
|
|
1434
|
+
<BbBadge variant="outline">{{ recent.status }}</BbBadge>
|
|
1435
|
+
<span class="text-xs text-gray-500">{{
|
|
1436
|
+
currency.format(recent.amount)
|
|
1437
|
+
}}</span>
|
|
1438
|
+
</li>
|
|
1439
|
+
</ul>
|
|
1440
|
+
</section>
|
|
1441
|
+
|
|
1442
|
+
<footer class="mt-2 flex items-center gap-2 border-t pt-3">
|
|
1443
|
+
<BbButton
|
|
1444
|
+
append:icon="lucide:arrow-up-right"
|
|
1445
|
+
size="sm"
|
|
1446
|
+
:to="`/people/${submitter.id}`"
|
|
1447
|
+
variant="ghost"
|
|
1448
|
+
>
|
|
1449
|
+
Open profile
|
|
1450
|
+
</BbButton>
|
|
1451
|
+
</footer>
|
|
1452
|
+
</div>
|
|
1453
|
+
|
|
1454
|
+
<div v-else class="flex justify-center p-8">
|
|
1455
|
+
<BbSpinner />
|
|
1456
|
+
</div>
|
|
1457
|
+
</template>
|
|
1458
|
+
|
|
1459
|
+
<script setup lang="ts">
|
|
1460
|
+
import { ref, watchEffect } from 'vue';
|
|
1461
|
+
import { BbAvatar, BbBadge, BbButton, BbSpinner } from 'bitboss-ui';
|
|
1462
|
+
import { useApprovalsInbox } from './context';
|
|
1463
|
+
import type { SubmitterDetail } from '@/types';
|
|
1464
|
+
|
|
1465
|
+
const props = defineProps<{ submitterId: number }>();
|
|
1466
|
+
|
|
1467
|
+
const ctx = useApprovalsInbox();
|
|
1468
|
+
|
|
1469
|
+
const submitter = ref<SubmitterDetail | null>(null);
|
|
1470
|
+
watchEffect(async () => {
|
|
1471
|
+
submitter.value = await $fetch<SubmitterDetail>(
|
|
1472
|
+
`/api/submitters/${props.submitterId}`
|
|
1473
|
+
);
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1476
|
+
const currency = new Intl.NumberFormat('en-US', {
|
|
1477
|
+
style: 'currency',
|
|
1478
|
+
currency: 'USD',
|
|
1479
|
+
});
|
|
1480
|
+
</script>
|
|
1481
|
+
```
|
|
1482
|
+
|
|
1483
|
+
## Why it is built this way
|
|
1484
|
+
|
|
1485
|
+
- **The context coopts, leaves consume.** `shell` and `table` are joined
|
|
1486
|
+
once in `provideApprovalsInbox()`; twelve components share filters,
|
|
1487
|
+
selection, highlight, the peek stack, the keyboard shortcuts and every
|
|
1488
|
+
action through one `useApprovalsInbox()` call.
|
|
1489
|
+
- **Highlight is the trigger, not a bespoke click handler.** Per the
|
|
1490
|
+
`BbTable` guide, `highlighted` is "a parallel, UI-intent state…
|
|
1491
|
+
independent of selection" that ships unstyled — this recipe is exactly
|
|
1492
|
+
the case the guide's Row Highlight section describes: a `watch` on
|
|
1493
|
+
`table.highlighted` is the entire wiring between "arrow to a row" and
|
|
1494
|
+
"the peek is open", and `keyboard-navigation`'s Enter/Space "activate…
|
|
1495
|
+
through the same path as a mouse click" means the mouse and the keyboard
|
|
1496
|
+
share one code path for free. This is unchanged from the Vue recipe
|
|
1497
|
+
because it reads the **library** table context (the widget's own state),
|
|
1498
|
+
which has nothing to do with the URL.
|
|
1499
|
+
- **The URL is the filter store, `useAsyncData` is the fetch.** Filters and
|
|
1500
|
+
tab are seeded from `route.query` once at setup, mirrored back with
|
|
1501
|
+
`navigateTo` on every change (debounced for free text), and
|
|
1502
|
+
`useAsyncData('expenses', …, { watch: [() => route.query, table.page] })`
|
|
1503
|
+
is the single refetch trigger — there is exactly one place data loads.
|
|
1504
|
+
- **The table context still owns paging, selection and highlight.**
|
|
1505
|
+
`useBbTableContext` is unchanged from the Vue recipe: it seeds `perPage`,
|
|
1506
|
+
`BbPagination` joins by `table-id`, `SelectionBar` reads `table.selected`,
|
|
1507
|
+
and the highlight watcher reads `table.highlighted` — only the _items_
|
|
1508
|
+
move from an items-function to a resolved `:items` array written by the
|
|
1509
|
+
page's own fetch.
|
|
1510
|
+
- **`item-value="id"`** is what lets `table.highlighted` (and
|
|
1511
|
+
`table.selected`) hold plain ids instead of whole objects — the keyboard
|
|
1512
|
+
handler and the peek both resolve the full `Expense` from `ctx.expenses`
|
|
1513
|
+
(the current page's fetched list), never from the table's internal row
|
|
1514
|
+
wrapper.
|
|
1515
|
+
- **One dispatch path per action, three entry points.** `approve`,
|
|
1516
|
+
`openReject` and `openSnooze` are called identically from `RowActions`,
|
|
1517
|
+
`ExpensePeek`'s footer, and the `A`/`R`/`S` keyboard handlers
|
|
1518
|
+
(`approveHighlighted` etc. are the same functions, resolved against
|
|
1519
|
+
`highlightedExpense` instead of a row prop) — there is exactly one place
|
|
1520
|
+
each action's fetch call, `refresh()` and toast live.
|
|
1521
|
+
- **Reason and date are dialogs, not confirms.** Per the pattern catalog,
|
|
1522
|
+
"anything needing input opens a `BbDialog`" — reject needs a typed
|
|
1523
|
+
reason, snooze needs a date, so both get a small dialog whose open-state
|
|
1524
|
+
is the subject itself (`ctx.dialogs.reject` holds the `Expense`, not a
|
|
1525
|
+
boolean). Bulk reject is destructive **and** needs input, so it layers a
|
|
1526
|
+
`useConfirm` on top of the dialog's own submit.
|
|
1527
|
+
- **The shortcut guard mirrors `useDeleteCmd`.** `isInteractiveElementFocused()`
|
|
1528
|
+
is copied verbatim from the Inertia Helpers guide's `useDeleteCmd` (which
|
|
1529
|
+
the guide notes "works on any platform") so typing a rejection reason
|
|
1530
|
+
never fires `R`; the handler also bails while any dialog is open.
|
|
1531
|
+
- **`peekX` vs `stackX` is the whole stack grammar**, unchanged from the
|
|
1532
|
+
Vue recipe: highlighting always **replaces** (`peekExpense`, `true`),
|
|
1533
|
+
clicking a submitter's name always **pushes** (`stackSubmitter`), and the
|
|
1534
|
+
shell renders the back arrow once depth > 1 — from a row, from inside
|
|
1535
|
+
the expense peek, or drilling back from the submitter peek's own recent
|
|
1536
|
+
expenses.
|
|
1537
|
+
- **Peeks re-fetch off the list, not a manual counter.** `ExpensePeek`
|
|
1538
|
+
watches `ctx.expenses.value` (bumped by every `refresh()` call) instead
|
|
1539
|
+
of a separate `refreshKey` ref — one fewer piece of state, since the
|
|
1540
|
+
list's own reactivity already carries the "something changed" signal.
|
|
1541
|
+
- **Sizing:** topbar and rail follow `md`-first; `sm`/`xs` appear exactly
|
|
1542
|
+
where the doctrine allows — table rows (`xs`, the densest surface), the
|
|
1543
|
+
shortcut bar (`xs`), the peek footers and the bulk bar (`sm`).
|
|
1544
|
+
- **No `variant="primary"` on this page.** Triage has no "create" action —
|
|
1545
|
+
the one-primary-action rule is satisfied vacuously; every action here is
|
|
1546
|
+
either a one-click mutation, a gated mutation via dialog, or navigation.
|
|
1547
|
+
|
|
1548
|
+
## Variations
|
|
1549
|
+
|
|
1550
|
+
- **No snooze**: drop `SnoozeDialog.vue`, the `S` shortcut, and the
|
|
1551
|
+
`snoozed` tab state — the remaining approve/reject pair still clears the
|
|
1552
|
+
≥ 12 action bar on its own via the bulk/keyboard/peek spread.
|
|
1553
|
+
- **Auto-advance after action**: after `approve`/`reject` resolves, call
|
|
1554
|
+
`table.highlighted.value = nextExpenseId` (the id following the acted-on
|
|
1555
|
+
row in `ctx.expenses.value`) so triage keeps moving without a
|
|
1556
|
+
re-highlight — the classic "Superhuman" flow.
|
|
1557
|
+
- **Per-category routing**: seed `filters.categories` from a saved view
|
|
1558
|
+
per approver role (a store-backed default) instead of starting empty.
|
|
1559
|
+
- **SSR-populated peeks**: swap the peeks' `$fetch` calls for
|
|
1560
|
+
`useAsyncData` keyed by id if the peek content should be part of the
|
|
1561
|
+
server-rendered payload rather than a client-side follow-up fetch.
|