@meeovi/layer-shared 1.0.0
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/README.md +0 -0
- package/app/components/Gallery/Gallery.vue +187 -0
- package/app/components/Gallery/__tests__/Gallery.spec.ts +14 -0
- package/app/components/Heading/Heading.vue +14 -0
- package/app/components/Heading/__tests__/Heading.spec.ts +14 -0
- package/app/components/Heading/types.ts +5 -0
- package/app/components/media/audioGallery.vue +70 -0
- package/app/components/media/dragDropUpload.vue +67 -0
- package/app/components/media/fullscreenMediaModal.vue +66 -0
- package/app/components/media/imageCard.vue +65 -0
- package/app/components/media/imageGallery.vue +40 -0
- package/app/components/media/mediaCard.vue +89 -0
- package/app/components/media/mediaCarousel.vue +65 -0
- package/app/components/media/mediaFolderSidebar.vue +72 -0
- package/app/components/media/mediaPlayer.vue +40 -0
- package/app/components/media/mediaSearchBar.vue +16 -0
- package/app/components/media/videoGallery.vue +77 -0
- package/app/components/ui/AccordionItem/AccordionItem.vue +24 -0
- package/app/components/ui/AccordionItem/__tests__/AccordionItem.spec.ts +14 -0
- package/app/components/ui/AccordionItem/types.ts +5 -0
- package/app/components/ui/Alert/Alert.vue +34 -0
- package/app/components/ui/Alert/types.ts +5 -0
- package/app/components/ui/Breadcrumbs/Breadcrumbs.vue +76 -0
- package/app/components/ui/Breadcrumbs/__tests__/Breadcrumbs.spec.ts +14 -0
- package/app/components/ui/Breadcrumbs/types.ts +8 -0
- package/app/components/ui/CartProductCard/CartProductCard.vue +66 -0
- package/app/components/ui/CartProductCard/types.ts +18 -0
- package/app/components/ui/CategoryCard/CategoryCard.vue +41 -0
- package/app/components/ui/CategoryCard/types.ts +9 -0
- package/app/components/ui/Display/Display.vue +55 -0
- package/app/components/ui/Display/types.ts +12 -0
- package/app/components/ui/Divider/Divider.vue +3 -0
- package/app/components/ui/Divider/__tests__/Divider.spec.tsx +10 -0
- package/app/components/ui/Footer.vue +92 -0
- package/app/components/ui/Form/FormHelperText.vue +5 -0
- package/app/components/ui/Form/FormLabel.vue +5 -0
- package/app/components/ui/Form/FormPasswordInput.vue +15 -0
- package/app/components/ui/Form/__tests__/FormHelperText.spec.ts +10 -0
- package/app/components/ui/Form/__tests__/FormLabel.spec.ts +10 -0
- package/app/components/ui/Hero/Hero.vue +44 -0
- package/app/components/ui/Hero/types.ts +10 -0
- package/app/components/ui/Modal/Modal.vue +19 -0
- package/app/components/ui/Modal/types.ts +8 -0
- package/app/components/ui/Motionable.vue +45 -0
- package/app/components/ui/NavbarBottom.vue +63 -0
- package/app/components/ui/NavbarTop.vue +25 -0
- package/app/components/ui/Overlay/Overlay.vue +14 -0
- package/app/components/ui/Overlay/__tests__/Overlay.spec.ts +14 -0
- package/app/components/ui/Overlay/types.ts +3 -0
- package/app/components/ui/PageBuilder.vue +39 -0
- package/app/components/ui/PageContainer.vue +5 -0
- package/app/components/ui/Pagination/Pagination.vue +151 -0
- package/app/components/ui/Pagination/__tests__/Pagination.spec.ts +17 -0
- package/app/components/ui/Pagination/types.ts +6 -0
- package/app/components/ui/ProductCard/ProductCard.vue +55 -0
- package/app/components/ui/ProductCard/__tests__/ProductCard.spec.ts +16 -0
- package/app/components/ui/ProductCard/types.ts +12 -0
- package/app/components/ui/ProductCardHorizontal/ProductCardHorizontal.vue +34 -0
- package/app/components/ui/ProductCardHorizontal/__tests__/ProductCardHorizontal.spec.ts +36 -0
- package/app/components/ui/ProductCardHorizontal/types.ts +8 -0
- package/app/components/ui/PurchaseCard/PurchaseCard.vue +109 -0
- package/app/components/ui/PurchaseCard/__tests__/PurchaseCard.spec.ts +15 -0
- package/app/components/ui/PurchaseCard/types.ts +5 -0
- package/app/components/ui/QuantitySelector/QuantitySelector.vue +69 -0
- package/app/components/ui/QuantitySelector/__tests__/QuantitySelector.spec.ts +15 -0
- package/app/components/ui/QuantitySelector/types.ts +5 -0
- package/app/components/ui/RadialProgress.vue +44 -0
- package/app/components/ui/Review/Review.vue +57 -0
- package/app/components/ui/Review/__tests__/Review.spec.ts +15 -0
- package/app/components/ui/Review/types.ts +5 -0
- package/app/components/ui/ScrollTop.vue +82 -0
- package/app/components/ui/Search.vue +54 -0
- package/app/components/ui/Tag/Tag.vue +38 -0
- package/app/components/ui/Tag/__tests__/Tag.spec.ts +10 -0
- package/app/components/ui/Tag/types.ts +16 -0
- package/app/components/ui/VsfLogo.vue +7 -0
- package/app/components/ui/forms/BooleanInput.vue +34 -0
- package/app/components/ui/forms/DateTime.vue +44 -0
- package/app/components/ui/forms/DirectusFormElement.vue +60 -0
- package/app/components/ui/forms/DynamicTableElement.vue +57 -0
- package/app/components/ui/forms/FileInput.vue +85 -0
- package/app/components/ui/forms/FormField.vue +34 -0
- package/app/components/ui/forms/RelationSelect.vue +63 -0
- package/app/components/ui/forms/RepeaterInput.vue +121 -0
- package/app/components/ui/forms/SelectInput.vue +65 -0
- package/app/components/ui/forms/TextArea.vue +59 -0
- package/app/components/ui/forms/TextInput.vue +42 -0
- package/app/components/ui/forms/TiptapEditor.vue +136 -0
- package/app/components/ui/forms/[collection].vue +22 -0
- package/app/components/ui/studio/builder.vue +57 -0
- package/app/components/ui/studio/document.vue +69 -0
- package/app/components/ui/studio/email.vue +57 -0
- package/app/composables/globals/uploadFiles.js +41 -0
- package/app/composables/globals/useAdminTable.ts +12 -0
- package/app/composables/globals/useCustomFetch.ts +13 -0
- package/app/composables/globals/useDirectusField.ts +144 -0
- package/app/composables/globals/useDirectusForm.ts +70 -0
- package/app/composables/globals/useDirectusSchema.js +9 -0
- package/app/composables/globals/useFileManager.ts +76 -0
- package/app/composables/globals/useLivePreview.ts +17 -0
- package/app/composables/globals/useLoading.ts +23 -0
- package/app/composables/globals/useNavigation.js +19 -0
- package/app/composables/globals/useNotifications.ts +153 -0
- package/app/composables/globals/usePages.js +36 -0
- package/app/composables/globals/useRichText.ts +33 -0
- package/app/composables/globals/useServerRootMixin.ts +19 -0
- package/app/composables/globals/useVisualEditing.ts +38 -0
- package/app/composables/media/useFile.ts +6 -0
- package/app/composables/media/useMediaCenter.ts +353 -0
- package/app/composables/media/useVideojs.ts +45 -0
- package/app/composables/registry.ts +13 -0
- package/app/composables/types.ts +12 -0
- package/app/composables/useContent.ts +13 -0
- package/app/composables/useDirectusRequest.ts +32 -0
- package/app/stores/index.ts +0 -0
- package/app/types/api/global-search.ts +8 -0
- package/app/types/blocks/block-button-group.ts +7 -0
- package/app/types/blocks/block-button.ts +14 -0
- package/app/types/blocks/block-column.ts +20 -0
- package/app/types/blocks/block-cta.ts +10 -0
- package/app/types/blocks/block-divider.ts +4 -0
- package/app/types/blocks/block-faq.ts +12 -0
- package/app/types/blocks/block-form.ts +8 -0
- package/app/types/blocks/block-gallery.ts +14 -0
- package/app/types/blocks/block-hero.ts +12 -0
- package/app/types/blocks/block-html.ts +4 -0
- package/app/types/blocks/block-logocloud.ts +14 -0
- package/app/types/blocks/block-quote.ts +11 -0
- package/app/types/blocks/block-richtext.ts +7 -0
- package/app/types/blocks/block-steps.ts +22 -0
- package/app/types/blocks/block-team.ts +6 -0
- package/app/types/blocks/block-testimonial.ts +14 -0
- package/app/types/blocks/block-video.ts +10 -0
- package/app/types/blocks/block.ts +49 -0
- package/app/types/blocks/index.ts +18 -0
- package/app/types/componentMap.ts +15 -0
- package/app/types/content/category.ts +11 -0
- package/app/types/content/form.ts +20 -0
- package/app/types/content/index.ts +6 -0
- package/app/types/content/page.ts +76 -0
- package/app/types/content/post.ts +39 -0
- package/app/types/content/team.ts +16 -0
- package/app/types/content/testimonial.ts +19 -0
- package/app/types/directus.d.ts +47 -0
- package/app/types/env.d.ts +8 -0
- package/app/types/help/index.ts +53 -0
- package/app/types/index.d.ts +9 -0
- package/app/types/index.ts +7 -0
- package/app/types/meta/analytics.ts +18 -0
- package/app/types/meta/config.ts +21 -0
- package/app/types/meta/globals.ts +30 -0
- package/app/types/meta/index.ts +6 -0
- package/app/types/meta/navigation.ts +32 -0
- package/app/types/meta/redirect.ts +13 -0
- package/app/types/meta/seo.ts +19 -0
- package/app/types/os/contact.ts +23 -0
- package/app/types/os/conversation.ts +25 -0
- package/app/types/os/index.ts +16 -0
- package/app/types/os/organization.ts +54 -0
- package/app/types/os/os-activity.ts +28 -0
- package/app/types/os/os-deal.ts +45 -0
- package/app/types/os/os-expense.ts +22 -0
- package/app/types/os/os-invoice.ts +48 -0
- package/app/types/os/os-item.ts +18 -0
- package/app/types/os/os-payment.ts +29 -0
- package/app/types/os/os-project.ts +47 -0
- package/app/types/os/os-proposal.ts +84 -0
- package/app/types/os/os-settings.ts +19 -0
- package/app/types/os/os-subscription.ts +12 -0
- package/app/types/os/os-task.ts +34 -0
- package/app/types/os/os-tax-rate.ts +13 -0
- package/app/types/pageComponentMap.ts +8 -0
- package/app/types/schema.d.ts +39 -0
- package/app/types/schema.ts +151 -0
- package/app/types/system/file.ts +46 -0
- package/app/types/system/folder.ts +8 -0
- package/app/types/system/index.ts +4 -0
- package/app/types/system/role.ts +21 -0
- package/app/types/system/user.ts +56 -0
- package/app/utils/Timer.js +44 -0
- package/app/utils/billing-address.ts +24 -0
- package/app/utils/color.ts +14 -0
- package/app/utils/currency.ts +29 -0
- package/app/utils/embed.ts +57 -0
- package/app/utils/errors.ts +9 -0
- package/app/utils/fieldRegistry.js +89 -0
- package/app/utils/fonts.ts +24 -0
- package/app/utils/formkit.ts +75 -0
- package/app/utils/icons.ts +62 -0
- package/app/utils/index.js +0 -0
- package/app/utils/links.ts +28 -0
- package/app/utils/lodash.ts +33 -0
- package/app/utils/markdown.ts +9 -0
- package/app/utils/math.ts +25 -0
- package/app/utils/navigation.ts +11 -0
- package/app/utils/objects.ts +11 -0
- package/app/utils/paths.ts +21 -0
- package/app/utils/relations.ts +33 -0
- package/app/utils/strings.ts +113 -0
- package/app/utils/time.ts +148 -0
- package/app/utils/url.ts +22 -0
- package/app/utils/user-name.ts +21 -0
- package/app/utils/video/README.md +51 -0
- package/app/utils/video/playlist.js +0 -0
- package/dist/api/global-search.d.ts +8 -0
- package/dist/api/global-search.js +1 -0
- package/dist/blocks/block-button-group.d.ts +6 -0
- package/dist/blocks/block-button-group.js +1 -0
- package/dist/blocks/block-button.d.ts +13 -0
- package/dist/blocks/block-button.js +1 -0
- package/dist/blocks/block-column.d.ts +18 -0
- package/dist/blocks/block-column.js +1 -0
- package/dist/blocks/block-cta.d.ts +11 -0
- package/dist/blocks/block-cta.js +1 -0
- package/dist/blocks/block-divider.d.ts +4 -0
- package/dist/blocks/block-divider.js +1 -0
- package/dist/blocks/block-faq.d.ts +11 -0
- package/dist/blocks/block-faq.js +1 -0
- package/dist/blocks/block-form.d.ts +7 -0
- package/dist/blocks/block-form.js +1 -0
- package/dist/blocks/block-gallery.d.ts +13 -0
- package/dist/blocks/block-gallery.js +1 -0
- package/dist/blocks/block-hero.d.ts +11 -0
- package/dist/blocks/block-hero.js +1 -0
- package/dist/blocks/block-html.d.ts +4 -0
- package/dist/blocks/block-html.js +1 -0
- package/dist/blocks/block-logocloud.d.ts +13 -0
- package/dist/blocks/block-logocloud.js +1 -0
- package/dist/blocks/block-quote.d.ts +10 -0
- package/dist/blocks/block-quote.js +1 -0
- package/dist/blocks/block-richtext.d.ts +7 -0
- package/dist/blocks/block-richtext.js +1 -0
- package/dist/blocks/block-steps.d.ts +21 -0
- package/dist/blocks/block-steps.js +1 -0
- package/dist/blocks/block-team.d.ts +6 -0
- package/dist/blocks/block-team.js +1 -0
- package/dist/blocks/block-testimonial.d.ts +13 -0
- package/dist/blocks/block-testimonial.js +1 -0
- package/dist/blocks/block-video.d.ts +9 -0
- package/dist/blocks/block-video.js +1 -0
- package/dist/blocks/block.d.ts +17 -0
- package/dist/blocks/block.js +1 -0
- package/dist/blocks/index.d.ts +18 -0
- package/dist/blocks/index.js +1 -0
- package/dist/componentMap.d.ts +6 -0
- package/dist/componentMap.js +8 -0
- package/dist/content/category.d.ts +10 -0
- package/dist/content/category.js +1 -0
- package/dist/content/form.d.ts +21 -0
- package/dist/content/form.js +1 -0
- package/dist/content/index.d.ts +6 -0
- package/dist/content/index.js +1 -0
- package/dist/content/page.d.ts +38 -0
- package/dist/content/page.js +1 -0
- package/dist/content/post.d.ts +38 -0
- package/dist/content/post.js +1 -0
- package/dist/content/team.d.ts +17 -0
- package/dist/content/team.js +1 -0
- package/dist/content/testimonial.d.ts +18 -0
- package/dist/content/testimonial.js +1 -0
- package/dist/help/index.d.ts +51 -0
- package/dist/help/index.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -0
- package/dist/meta/analytics.d.ts +21 -0
- package/dist/meta/analytics.js +1 -0
- package/dist/meta/config.d.ts +22 -0
- package/dist/meta/config.js +1 -0
- package/dist/meta/globals.d.ts +33 -0
- package/dist/meta/globals.js +1 -0
- package/dist/meta/index.d.ts +6 -0
- package/dist/meta/index.js +1 -0
- package/dist/meta/navigation.d.ts +31 -0
- package/dist/meta/navigation.js +1 -0
- package/dist/meta/redirect.d.ts +12 -0
- package/dist/meta/redirect.js +1 -0
- package/dist/meta/seo.d.ts +19 -0
- package/dist/meta/seo.js +1 -0
- package/dist/os/contact.d.ts +22 -0
- package/dist/os/contact.js +1 -0
- package/dist/os/conversation.d.ts +23 -0
- package/dist/os/conversation.js +1 -0
- package/dist/os/index.d.ts +16 -0
- package/dist/os/index.js +1 -0
- package/dist/os/organization.d.ts +51 -0
- package/dist/os/organization.js +1 -0
- package/dist/os/os-activity.d.ts +26 -0
- package/dist/os/os-activity.js +1 -0
- package/dist/os/os-deal.d.ts +42 -0
- package/dist/os/os-deal.js +1 -0
- package/dist/os/os-expense.d.ts +21 -0
- package/dist/os/os-expense.js +1 -0
- package/dist/os/os-invoice.d.ts +46 -0
- package/dist/os/os-invoice.js +1 -0
- package/dist/os/os-item.d.ts +17 -0
- package/dist/os/os-item.js +1 -0
- package/dist/os/os-payment.d.ts +27 -0
- package/dist/os/os-payment.js +1 -0
- package/dist/os/os-project.d.ts +45 -0
- package/dist/os/os-project.js +1 -0
- package/dist/os/os-proposal.d.ts +61 -0
- package/dist/os/os-proposal.js +1 -0
- package/dist/os/os-settings.d.ts +17 -0
- package/dist/os/os-settings.js +1 -0
- package/dist/os/os-subscription.d.ts +12 -0
- package/dist/os/os-subscription.js +1 -0
- package/dist/os/os-task.d.ts +32 -0
- package/dist/os/os-task.js +1 -0
- package/dist/os/os-tax-rate.d.ts +12 -0
- package/dist/os/os-tax-rate.js +1 -0
- package/dist/pageComponentMap.d.ts +2 -0
- package/dist/pageComponentMap.js +7 -0
- package/dist/schema.d.ts +78 -0
- package/dist/schema.js +1 -0
- package/dist/system/file.d.ts +47 -0
- package/dist/system/file.js +1 -0
- package/dist/system/folder.d.ts +8 -0
- package/dist/system/folder.js +1 -0
- package/dist/system/index.d.ts +4 -0
- package/dist/system/index.js +1 -0
- package/dist/system/role.d.ts +20 -0
- package/dist/system/role.js +1 -0
- package/dist/system/user.d.ts +57 -0
- package/dist/system/user.js +1 -0
- package/nuxt.config.ts +5 -0
- package/package.json +42 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Folder } from './folder.js';
|
|
2
|
+
import type { User } from './user.js';
|
|
3
|
+
|
|
4
|
+
export interface File {
|
|
5
|
+
/** Unique identifier for the file. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Where the file is stored. Either `local` for the local filesystem or the name of the storage adapter (for example `s3`). */
|
|
8
|
+
storage: string;
|
|
9
|
+
/** Name of the file on disk. By default, Directus uses a random hash for the filename. */
|
|
10
|
+
filename_disk: string;
|
|
11
|
+
/** How you want to the file to be named when it's being downloaded. */
|
|
12
|
+
filename_download: string;
|
|
13
|
+
/** Title for the file. Is extracted from the filename on upload, but can be edited by the user. */
|
|
14
|
+
title: string;
|
|
15
|
+
/** MIME type of the file. */
|
|
16
|
+
type: string;
|
|
17
|
+
/** Virtual folder where this file resides in. */
|
|
18
|
+
folder: string | Folder | null;
|
|
19
|
+
/** Who uploaded the file. */
|
|
20
|
+
uploaded_by: string | User;
|
|
21
|
+
/** When the file was uploaded. */
|
|
22
|
+
uploaded_on: string;
|
|
23
|
+
modified_by: string | User | null;
|
|
24
|
+
modified_on: string;
|
|
25
|
+
/** Character set of the file. */
|
|
26
|
+
charset: string | null;
|
|
27
|
+
/** Size of the file in bytes. */
|
|
28
|
+
filesize: number;
|
|
29
|
+
/** Width of the file in pixels. Only applies to images. */
|
|
30
|
+
width: number | null;
|
|
31
|
+
/** Height of the file in pixels. Only applies to images. */
|
|
32
|
+
height: number | null;
|
|
33
|
+
/** Duration of the file in seconds. Only applies to audio and video. */
|
|
34
|
+
duration: number | null;
|
|
35
|
+
/** Where the file was embedded from. */
|
|
36
|
+
embed: string | null;
|
|
37
|
+
/** Description for the file. */
|
|
38
|
+
description: string | null;
|
|
39
|
+
/** Where the file was created. Is automatically populated based on EXIF data for images. */
|
|
40
|
+
location: string | null;
|
|
41
|
+
/** Tags for the file. Is automatically populated based on EXIF data for images. */
|
|
42
|
+
tags: string[] | null;
|
|
43
|
+
/** IPTC, EXIF, and ICC metadata extracted from file */
|
|
44
|
+
metadata: { [key: string]: any } | null;
|
|
45
|
+
storage_divider: string;
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { User } from './user.js';
|
|
2
|
+
|
|
3
|
+
export interface Role {
|
|
4
|
+
/** Unique identifier for the role. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Name of the role. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** The role's icon. */
|
|
9
|
+
icon: string;
|
|
10
|
+
/** Description of the role. */
|
|
11
|
+
description: string | null;
|
|
12
|
+
/** Array of IP addresses that are allowed to connect to the API as a user of this role. */
|
|
13
|
+
ip_access: string[];
|
|
14
|
+
/** Whether or not this role enforces the use of 2FA. */
|
|
15
|
+
enforce_tfa: boolean;
|
|
16
|
+
/** Admin role. If true, skips all permission checks. */
|
|
17
|
+
admin_access: boolean;
|
|
18
|
+
/** The users in the role are allowed to use the app. */
|
|
19
|
+
app_access: boolean;
|
|
20
|
+
users: string | User[];
|
|
21
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Role } from './role.js';
|
|
2
|
+
|
|
3
|
+
export interface User {
|
|
4
|
+
/** Unique identifier for the user. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** First name of the user. */
|
|
7
|
+
first_name: string;
|
|
8
|
+
/** Last name of the user. */
|
|
9
|
+
last_name: string;
|
|
10
|
+
/** Unique email address for the user. */
|
|
11
|
+
email: string;
|
|
12
|
+
/** Password of the user. */
|
|
13
|
+
password: string;
|
|
14
|
+
/** The user's location. */
|
|
15
|
+
location: string | null;
|
|
16
|
+
/** The user's title. */
|
|
17
|
+
title: string | null;
|
|
18
|
+
/** The user's description. */
|
|
19
|
+
description: string | null;
|
|
20
|
+
/** The user's tags. */
|
|
21
|
+
tags: string[] | null;
|
|
22
|
+
/** The user's avatar. */
|
|
23
|
+
avatar: string | File | null;
|
|
24
|
+
/** The user's language used in Directus. */
|
|
25
|
+
language: string;
|
|
26
|
+
/** What theme the user is using. */
|
|
27
|
+
theme: 'light' | 'dark' | 'auto';
|
|
28
|
+
/** The 2FA secret string that's used to generate one time passwords. */
|
|
29
|
+
tfa_secret: string | null;
|
|
30
|
+
/** Status of the user. */
|
|
31
|
+
status: 'active' | 'invited' | 'draft' | 'suspended' | 'deleted';
|
|
32
|
+
/** Unique identifier of the role of this user. */
|
|
33
|
+
role: string | Role;
|
|
34
|
+
/** Static token for the user. */
|
|
35
|
+
token: string | null;
|
|
36
|
+
last_access: string | null;
|
|
37
|
+
/** Last page that the user was on. */
|
|
38
|
+
last_page: string | null;
|
|
39
|
+
provider: string;
|
|
40
|
+
external_identifier: string | null;
|
|
41
|
+
auth_data: { [key: string]: any } | null;
|
|
42
|
+
github: string | null;
|
|
43
|
+
linkedin: string | null;
|
|
44
|
+
start_date: string | null;
|
|
45
|
+
pronouns: string | null;
|
|
46
|
+
nickname: string | null;
|
|
47
|
+
birthday: string | null;
|
|
48
|
+
personal_email: string | null;
|
|
49
|
+
classification: string | null;
|
|
50
|
+
timezone: string | null;
|
|
51
|
+
phone: string | null;
|
|
52
|
+
email_notifications: boolean | null;
|
|
53
|
+
company_info: string;
|
|
54
|
+
preferences_divider: string;
|
|
55
|
+
admin_divider: string;
|
|
56
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const ONE_SECONDS = 1000;
|
|
2
|
+
|
|
3
|
+
export default class Timer {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.currentInSeconds = 0;
|
|
6
|
+
this.timer = null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static convertToMillisecs(d = "0") {
|
|
10
|
+
return d.split(":").reverse().map((e) => +e).map((e, i) => (i === 0 ? e : e * Math.pow(60, i)) * 1000).reduce((p, c) => p + c, 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static toTimeString(timeInMillisecs) {
|
|
14
|
+
const c = new Date(timeInMillisecs);
|
|
15
|
+
|
|
16
|
+
const t = [
|
|
17
|
+
c.getHours() - 1,
|
|
18
|
+
c.getMinutes(),
|
|
19
|
+
c.getSeconds()
|
|
20
|
+
]
|
|
21
|
+
return t.map((e) => ("" + e).padStart(2, "0")).join(":")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
start(_at = 0, cb = (time) => {}) {
|
|
25
|
+
let _atInMillisecs = _at
|
|
26
|
+
if (typeof _at === "string") {
|
|
27
|
+
_atInMillisecs = Timer.convertToMillisecs(_at)
|
|
28
|
+
};
|
|
29
|
+
this.currentInSeconds = _atInMillisecs;
|
|
30
|
+
|
|
31
|
+
this.timer = setInterval(() => {
|
|
32
|
+
this.currentInSeconds += ONE_SECONDS;
|
|
33
|
+
cb(this.currentInSeconds)
|
|
34
|
+
}, ONE_SECONDS)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
stop() {
|
|
38
|
+
clearInterval(this.timer)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
reset () {
|
|
42
|
+
this.currentInSeconds = 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Organization, OrganizationAddress } from '../types';
|
|
2
|
+
|
|
3
|
+
export function getPrimaryBillingAddress(organization: Organization): OrganizationAddress | null {
|
|
4
|
+
if (!organization || !organization.addresses || !Array.isArray(organization.addresses)) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (organization.addresses.length === 1) {
|
|
9
|
+
const address = organization.addresses[0];
|
|
10
|
+
return typeof address === 'string' ? null : address;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for (const address of organization.addresses) {
|
|
14
|
+
if (typeof address === 'string') {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (address.is_primary_billing) {
|
|
19
|
+
return address;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function getContrastColor(hexColor: string) {
|
|
2
|
+
// Convert hex color to RGB format
|
|
3
|
+
const red = parseInt(hexColor.slice(1, 3), 16);
|
|
4
|
+
const green = parseInt(hexColor.slice(3, 5), 16);
|
|
5
|
+
const blue = parseInt(hexColor.slice(5, 7), 16);
|
|
6
|
+
|
|
7
|
+
// Calculate relative luminance of the color
|
|
8
|
+
const luminance = (0.2126 * red + 0.7152 * green + 0.0722 * blue) / 255;
|
|
9
|
+
|
|
10
|
+
// Return black or white depending on the luminance value
|
|
11
|
+
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { getContrastColor };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const formatCurrency = (
|
|
2
|
+
number: number,
|
|
3
|
+
options?: {
|
|
4
|
+
hideZeros?: boolean;
|
|
5
|
+
},
|
|
6
|
+
) => {
|
|
7
|
+
const formatter = new Intl.NumberFormat('en-US', {
|
|
8
|
+
style: 'currency',
|
|
9
|
+
currency: 'USD',
|
|
10
|
+
// These options are needed to round to whole numbers if that's what you want.
|
|
11
|
+
...(options &&
|
|
12
|
+
options.hideZeros && {
|
|
13
|
+
minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
|
|
14
|
+
maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501))
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return formatter.format(number);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function centsToDollars(cents: number) {
|
|
22
|
+
return Number((cents / 100).toFixed(2));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function dollarsToCents(dollars: number) {
|
|
26
|
+
return Math.round(dollars * 100);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { formatCurrency, centsToDollars, dollarsToCents };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
type UrlTransformation = {
|
|
2
|
+
pattern: RegExp;
|
|
3
|
+
replacement: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const transformations: UrlTransformation[] = [
|
|
7
|
+
// Video Players
|
|
8
|
+
{
|
|
9
|
+
pattern: /https:\/\/www\.youtube\.com\/watch\?v=(.+)/,
|
|
10
|
+
replacement: 'https://www.youtube.com/embed/$1',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
pattern: /https:\/\/vimeo\.com\/(.+)/,
|
|
14
|
+
replacement: 'https://player.vimeo.com/video/$1',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
pattern: /https:\/\/www\.loom\.com\/share\/(.+)/,
|
|
18
|
+
replacement: 'https://www.loom.com/embed/$1',
|
|
19
|
+
},
|
|
20
|
+
// File Services
|
|
21
|
+
{
|
|
22
|
+
pattern: /https:\/\/drive\.google\.com\/file\/d\/(.*?)\/view/,
|
|
23
|
+
replacement: 'https://drive.google.com/file/d/$1/preview',
|
|
24
|
+
},
|
|
25
|
+
// Calendar Tools
|
|
26
|
+
// Calendly: Remains unchanged
|
|
27
|
+
// SavvyCal: Remains unchanged
|
|
28
|
+
// Hubspot: @TODO
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export function transformUrlToIframeSrc(url: string): string {
|
|
32
|
+
for (const transformation of transformations) {
|
|
33
|
+
if (transformation.pattern.test(url)) {
|
|
34
|
+
return url.replace(transformation.pattern, transformation.replacement);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If no transformation matches, return the original URL.
|
|
39
|
+
return url;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//Generate the Youtube, Vimeo, or Loom embed URL based on an URL from the user
|
|
43
|
+
export function generateVideoEmbed(url: string) {
|
|
44
|
+
if (url.includes('youtube.com')) {
|
|
45
|
+
return `https://www.youtube.com/embed/${url.split('v=')[1]}`;
|
|
46
|
+
}
|
|
47
|
+
// Handle youtu.be links
|
|
48
|
+
else if (url.includes('youtu.be')) {
|
|
49
|
+
return `https://www.youtube.com/embed/${url.split('be/')[1]}`;
|
|
50
|
+
} else if (url.includes('vimeo.com')) {
|
|
51
|
+
return `https://player.vimeo.com/video/${url.split('vimeo.com/')[1]}`;
|
|
52
|
+
} else if (url.includes('loom.com')) {
|
|
53
|
+
return `https://www.loom.com/embed/${url.split('loom.com/share/')[1]}`;
|
|
54
|
+
} else {
|
|
55
|
+
return url;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ZodError } from 'zod'
|
|
2
|
+
import { isNuxtError, type NuxtError } from '#imports'
|
|
3
|
+
|
|
4
|
+
export function isNuxtZodError(err: unknown): err is NuxtError<{ data: ZodError }> {
|
|
5
|
+
return (
|
|
6
|
+
isNuxtError(err)
|
|
7
|
+
&& (err.data as { data?: unknown })?.data instanceof ZodError
|
|
8
|
+
)
|
|
9
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { defineAsyncComponent, markRaw } from 'vue'
|
|
2
|
+
import TextInput from '~/components/blocks/forms/TextInput.vue'
|
|
3
|
+
import TextArea from '~/components/blocks/forms/TextArea.vue'
|
|
4
|
+
import SelectInput from '~/components/blocks/forms/SelectInput.vue'
|
|
5
|
+
import BooleanInput from '~/components/blocks/forms/BooleanInput.vue'
|
|
6
|
+
import DateTime from '~/components/blocks/forms/DateTime.vue'
|
|
7
|
+
import FileInput from '~/components/blocks/forms/FileInput.vue'
|
|
8
|
+
|
|
9
|
+
// Registry keyed by Directus schema.type (fallback)
|
|
10
|
+
export const fieldRegistry = {
|
|
11
|
+
string: TextInput,
|
|
12
|
+
text: TextArea,
|
|
13
|
+
datetime: DateTime,
|
|
14
|
+
file: FileInput,
|
|
15
|
+
image: FileInput,
|
|
16
|
+
video: FileInput,
|
|
17
|
+
audio: FileInput,
|
|
18
|
+
document: FileInput,
|
|
19
|
+
archive: FileInput,
|
|
20
|
+
enum: SelectInput,
|
|
21
|
+
boolean: BooleanInput
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Registry keyed by Directus field.meta.interface (preferred)
|
|
25
|
+
export const interfaceRegistry = {
|
|
26
|
+
// basic inputs
|
|
27
|
+
'input': TextInput,
|
|
28
|
+
'input-text': TextInput,
|
|
29
|
+
'input-rich-text': TextArea,
|
|
30
|
+
'input-rich-text-md': TextArea,
|
|
31
|
+
'textarea': TextArea,
|
|
32
|
+
|
|
33
|
+
// selects
|
|
34
|
+
'select-dropdown': SelectInput,
|
|
35
|
+
'select-multiple': SelectInput,
|
|
36
|
+
'select-list': SelectInput,
|
|
37
|
+
|
|
38
|
+
// boolean / switches
|
|
39
|
+
'boolean': BooleanInput,
|
|
40
|
+
'toggle': BooleanInput,
|
|
41
|
+
|
|
42
|
+
// files
|
|
43
|
+
'file': FileInput,
|
|
44
|
+
'file-image': FileInput,
|
|
45
|
+
'file-upload': FileInput,
|
|
46
|
+
'file-input': FileInput,
|
|
47
|
+
|
|
48
|
+
// dates
|
|
49
|
+
'datetime': DateTime,
|
|
50
|
+
'date': DateTime,
|
|
51
|
+
'time': DateTime,
|
|
52
|
+
|
|
53
|
+
// tags / relational-ish
|
|
54
|
+
'tags': SelectInput,
|
|
55
|
+
|
|
56
|
+
// fallback placeholders for other common interfaces (lazy-loaded if custom components are added)
|
|
57
|
+
'gallery': markRaw(defineAsyncComponent(() => import('~/components/blocks/forms/FileInput.vue'))),
|
|
58
|
+
'color': TextInput,
|
|
59
|
+
'rating': TextInput,
|
|
60
|
+
'url': TextInput,
|
|
61
|
+
'email': TextInput,
|
|
62
|
+
'password': TextInput,
|
|
63
|
+
'markdown': TextArea,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the component to render for a Directus field.
|
|
68
|
+
* Priority:
|
|
69
|
+
* 1. field.meta.interface -> interfaceRegistry
|
|
70
|
+
* 2. field.type -> fieldRegistry
|
|
71
|
+
* 3. fallback to TextInput
|
|
72
|
+
*/
|
|
73
|
+
export function resolveFieldComponent(field = {}) {
|
|
74
|
+
// prefer interface from meta
|
|
75
|
+
const iface = field?.meta?.interface
|
|
76
|
+
if (iface && interfaceRegistry[iface]) return interfaceRegistry[iface]
|
|
77
|
+
|
|
78
|
+
// some interfaces include variants or capitalization differences, try normalized lookup
|
|
79
|
+
if (iface) {
|
|
80
|
+
const normalized = String(iface).toLowerCase()
|
|
81
|
+
if (interfaceRegistry[normalized]) return interfaceRegistry[normalized]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// fallback to type mapping
|
|
85
|
+
if (field?.type && fieldRegistry[field.type]) return fieldRegistry[field.type]
|
|
86
|
+
|
|
87
|
+
// last-resort: use TextInput
|
|
88
|
+
return TextInput
|
|
89
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type FontFamilies = {
|
|
2
|
+
[font: string]: boolean | number[];
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const formatFonts = (families: FontFamilies, defaultWeights: number[] = [400]): string[] => {
|
|
6
|
+
const formatted: string[] = [];
|
|
7
|
+
|
|
8
|
+
for (const font in families) {
|
|
9
|
+
const formattedFont = font.replace(/ /g, '+'); // Replace spaces with '+'
|
|
10
|
+
const weights = families[font];
|
|
11
|
+
|
|
12
|
+
if (weights === true) {
|
|
13
|
+
for (const weight of defaultWeights) {
|
|
14
|
+
formatted.push(`${formattedFont}:${weight}`);
|
|
15
|
+
}
|
|
16
|
+
} else if (Array.isArray(weights)) {
|
|
17
|
+
for (const weight of weights) {
|
|
18
|
+
formatted.push(`${formattedFont}:${weight}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return formatted;
|
|
24
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { unref } from 'vue';
|
|
2
|
+
|
|
3
|
+
export interface Condition {
|
|
4
|
+
field: string;
|
|
5
|
+
action: 'show' | 'hide';
|
|
6
|
+
condition: 'is_empty' | 'is_filled' | 'contains' | 'not_contains' | 'equals' | 'not_equal';
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function convertBoolean(value: string) {
|
|
11
|
+
if (value === 'true' || value === 'false') {
|
|
12
|
+
return value === 'true';
|
|
13
|
+
} else {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function mapCondition(condition: Condition['condition'], target: string) {
|
|
19
|
+
switch (condition) {
|
|
20
|
+
case 'is_empty':
|
|
21
|
+
return `!$get(${target}).value)`;
|
|
22
|
+
case 'is_filled':
|
|
23
|
+
return `$get(${target}).value)`;
|
|
24
|
+
default:
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function mapConditions(conditions: Condition[]) {
|
|
30
|
+
// Both $el and $cmp schema nodes can leverage an if property that roughly equates to a v-if in Vue. If the expression assigned to the if property is truthy, the node is rendered, otherwise it is not:
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function transformSchema(schema: Array<object>) {
|
|
34
|
+
// Loop through the form schema from Directus
|
|
35
|
+
// This is required for FormKit to work
|
|
36
|
+
const items = unref(schema);
|
|
37
|
+
return items.map((item: any) => {
|
|
38
|
+
const { conditions, field, name, children, ...props } = item;
|
|
39
|
+
|
|
40
|
+
// console.log('conditions', conditions);
|
|
41
|
+
// console.log('mapCondition', mapCondition(conditions[0].condition, field));
|
|
42
|
+
|
|
43
|
+
const cmpSchema = {
|
|
44
|
+
$cmp: item.$el ? item.$el : 'FormKit',
|
|
45
|
+
|
|
46
|
+
children: children,
|
|
47
|
+
// if: conditions ? mapCondition(conditions[0].condition, field) : undefined,
|
|
48
|
+
|
|
49
|
+
props: {
|
|
50
|
+
id: name,
|
|
51
|
+
...props,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Switch statement to handle item widths
|
|
56
|
+
switch (item.width) {
|
|
57
|
+
case '33':
|
|
58
|
+
cmpSchema.props.outerClass = 'md:col-span-2';
|
|
59
|
+
break;
|
|
60
|
+
case '50':
|
|
61
|
+
cmpSchema.props.outerClass = 'md:col-span-3';
|
|
62
|
+
break;
|
|
63
|
+
case '67':
|
|
64
|
+
cmpSchema.props.outerClass = 'md:col-span-4';
|
|
65
|
+
break;
|
|
66
|
+
case '100':
|
|
67
|
+
cmpSchema.props.outerClass = 'md:col-span-6';
|
|
68
|
+
break;
|
|
69
|
+
default:
|
|
70
|
+
cmpSchema.props.outerClass = 'md:col-span-6';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return cmpSchema;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
type FileType =
|
|
2
|
+
| 'folder'
|
|
3
|
+
| 'image/jpeg'
|
|
4
|
+
| 'image/png'
|
|
5
|
+
| 'image/gif'
|
|
6
|
+
| 'image/svg+xml'
|
|
7
|
+
| 'image/webp'
|
|
8
|
+
| 'video/mp4'
|
|
9
|
+
| 'video/quicktime'
|
|
10
|
+
| 'audio/mp3'
|
|
11
|
+
| 'audio/aac'
|
|
12
|
+
| 'audio/wav'
|
|
13
|
+
| 'audio/ogg'
|
|
14
|
+
| 'text/csv'
|
|
15
|
+
| 'text/plain'
|
|
16
|
+
| 'application/pdf'
|
|
17
|
+
| 'application/vnd.ms-excel'
|
|
18
|
+
| 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
19
|
+
| 'application/msword'
|
|
20
|
+
| 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
21
|
+
| 'application/vnd.ms-powerpoint'
|
|
22
|
+
| 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
|
23
|
+
|
|
24
|
+
export const fileIconMap: Record<FileType, string> = {
|
|
25
|
+
folder: 'material-symbols:folder',
|
|
26
|
+
// Images
|
|
27
|
+
'image/jpeg': 'material-symbols:image',
|
|
28
|
+
'image/png': 'material-symbols:image',
|
|
29
|
+
'image/gif': 'material-symbols:image',
|
|
30
|
+
'image/svg+xml': 'material-symbols:image',
|
|
31
|
+
'image/webp': 'material-symbols:image',
|
|
32
|
+
// Videos
|
|
33
|
+
'video/mp4': 'material-symbols:smart-display',
|
|
34
|
+
'video/quicktime': 'material-symbols:smart-display',
|
|
35
|
+
// Audio
|
|
36
|
+
'audio/mp3': 'material-symbols:audio-file',
|
|
37
|
+
'audio/aac': 'material-symbols:audio-file',
|
|
38
|
+
'audio/wav': 'material-symbols:audio-file',
|
|
39
|
+
'audio/ogg': 'material-symbols:audio-file',
|
|
40
|
+
// Text
|
|
41
|
+
'text/csv': 'material-symbols:csv',
|
|
42
|
+
'text/plain': 'material-symbols:text-snippet',
|
|
43
|
+
|
|
44
|
+
// Files
|
|
45
|
+
'application/pdf': 'material-symbols:picture-as-pdf-sharp',
|
|
46
|
+
'application/vnd.ms-excel': 'material-symbols:sheets',
|
|
47
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'material-symbols:sheets',
|
|
48
|
+
'application/msword': 'material-symbols:docs',
|
|
49
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'material-symbols:docs',
|
|
50
|
+
'application/vnd.ms-powerpoint': 'material-symbols:slides',
|
|
51
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'material-symbols:slides',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export function getFileIcon(filetype: FileType | string | null | undefined) {
|
|
55
|
+
if (!filetype) return 'material-symbols:attachment';
|
|
56
|
+
|
|
57
|
+
if (typeof filetype === 'string' && !(filetype in fileIconMap)) {
|
|
58
|
+
return 'material-symbols:attachment';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return fileIconMap[filetype as FileType];
|
|
62
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Function to easily format a mailto link. Encodes the subject and body to preserve formatting.
|
|
2
|
+
export function createMailto(
|
|
3
|
+
to: string,
|
|
4
|
+
{ subject, body, cc, bcc }: { subject?: string; body?: string; cc?: string; bcc?: string } = {},
|
|
5
|
+
): string {
|
|
6
|
+
const params = [
|
|
7
|
+
subject && `subject=${encodeURIComponent(subject)}`,
|
|
8
|
+
body && `body=${encodeURIComponent(body)}`,
|
|
9
|
+
cc && `cc=${encodeURIComponent(cc)}`,
|
|
10
|
+
bcc && `bcc=${encodeURIComponent(bcc)}`,
|
|
11
|
+
]
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
.join('&');
|
|
14
|
+
|
|
15
|
+
return `mailto:${to}?${params}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createTel(phoneNumber: string) {
|
|
19
|
+
// Remove all non-numeric characters
|
|
20
|
+
const cleanedNumber = phoneNumber.replace(/\D+/g, '');
|
|
21
|
+
|
|
22
|
+
// Check if the number has a country code (e.g., +1 for the US)
|
|
23
|
+
if (cleanedNumber.length > 10 && cleanedNumber.startsWith('1')) {
|
|
24
|
+
return `tel:+${cleanedNumber}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return `tel:${cleanedNumber}`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function omit<T extends Record<string, any>, K extends keyof T>(
|
|
2
|
+
object: T,
|
|
3
|
+
keysToOmit: K[] | any[],
|
|
4
|
+
): Pick<T, Exclude<keyof T, K>> {
|
|
5
|
+
const result = { ...object };
|
|
6
|
+
|
|
7
|
+
for (const key of keysToOmit) {
|
|
8
|
+
delete result[key];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function get(object: Record<string, any>, path: (string | number)[] | string, defaultValue?: any): any {
|
|
15
|
+
if (typeof path === 'string') {
|
|
16
|
+
path = path.split('.').map((key) => {
|
|
17
|
+
const numKey = Number(key);
|
|
18
|
+
return isNaN(numKey) ? key : numKey;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let result: any = object;
|
|
23
|
+
|
|
24
|
+
for (const key of path) {
|
|
25
|
+
if (result === undefined || result === null) {
|
|
26
|
+
return defaultValue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
result = result[key];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return result !== undefined ? result : defaultValue;
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function formatPercent(number: number) {
|
|
2
|
+
return Number((number * 100).toFixed(1)) + '%';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function percentChange(num1: number, num2: number) {
|
|
6
|
+
if (num1 === 0 || num2 === 0) return 0;
|
|
7
|
+
return (num1 - num2) / num1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function roundToDecimal(value: number | string, decimals: number | string) {
|
|
11
|
+
const val = typeof value === 'string' ? parseFloat(value) : value;
|
|
12
|
+
const dec = typeof decimals === 'string' ? parseInt(decimals, 10) : decimals;
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
return Number(Math.round(val + 'e' + dec) + 'e-' + dec);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isEven(n: number) {
|
|
18
|
+
return n % 2 === 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isOdd(n: number) {
|
|
22
|
+
return Math.abs(n % 2) === 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { formatPercent, percentChange, roundToDecimal, isEven, isOdd };
|