@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,70 @@
|
|
|
1
|
+
import { ref, provide } from 'vue'
|
|
2
|
+
import type { Ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
export function useDirectusForm(collectionName: string, fieldsRef: Ref<any[]>, opts?: { clearOnSuccess?: boolean, closeDialogRef?: Ref<boolean> }) {
|
|
5
|
+
const form = ref<Record<string, any>>({})
|
|
6
|
+
const formError = ref<string | null>(null)
|
|
7
|
+
const formSuccess = ref<string | null>(null)
|
|
8
|
+
|
|
9
|
+
// provide form context for DirectusFormElement children
|
|
10
|
+
provide('directusForm', {
|
|
11
|
+
form,
|
|
12
|
+
fields: fieldsRef,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const submitForm = async () => {
|
|
16
|
+
formError.value = null
|
|
17
|
+
formSuccess.value = null
|
|
18
|
+
|
|
19
|
+
// Validate form data against field validation rules
|
|
20
|
+
for (const field of fieldsRef.value) {
|
|
21
|
+
if (field.meta?.validation) {
|
|
22
|
+
try {
|
|
23
|
+
const validation = field.meta.validation
|
|
24
|
+
if (validation._and) {
|
|
25
|
+
for (const rule of validation._and) {
|
|
26
|
+
const fieldName = Object.keys(rule)[0]
|
|
27
|
+
if (!fieldName) continue
|
|
28
|
+
const ruleDef = (rule as any)[fieldName]
|
|
29
|
+
if (ruleDef && ruleDef._regex) {
|
|
30
|
+
const regex = new RegExp(ruleDef._regex)
|
|
31
|
+
const valueToTest = String(form.value[field.field] ?? '')
|
|
32
|
+
if (!regex.test(valueToTest)) {
|
|
33
|
+
formError.value = field.meta.validation_message || field.meta.field + ' failed validation'
|
|
34
|
+
console.error(
|
|
35
|
+
`Validation failed for ${field.field}: ${field.meta.validation_message || 'Invalid format'}`)
|
|
36
|
+
return // Stop submission if validation fails
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error(`Error parsing validation for ${field.field}:`, err)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { $directus, $createItem } = useNuxtApp() as any
|
|
48
|
+
const result = await $directus.request($createItem(collectionName, form.value))
|
|
49
|
+
if (result.error) {
|
|
50
|
+
formError.value = result.error.message
|
|
51
|
+
console.error(`Error creating ${collectionName}:`, result.error)
|
|
52
|
+
return // Stop submission if error occurs
|
|
53
|
+
}
|
|
54
|
+
formSuccess.value = `${collectionName} created successfully`
|
|
55
|
+
|
|
56
|
+
if (opts?.clearOnSuccess) {
|
|
57
|
+
form.value = {}
|
|
58
|
+
}
|
|
59
|
+
if (opts?.closeDialogRef) {
|
|
60
|
+
try { opts.closeDialogRef.value = false } catch (e) {}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
form,
|
|
66
|
+
formError,
|
|
67
|
+
formSuccess,
|
|
68
|
+
submitForm,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export interface FileManagerConfig {
|
|
2
|
+
maxSize?: number
|
|
3
|
+
allowedTypes?: string[]
|
|
4
|
+
onProgress?: (progress: number) => void
|
|
5
|
+
onSuccess?: (file: any) => void
|
|
6
|
+
onError?: (error: Error) => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useFileManager(config: FileManagerConfig = {}) {
|
|
10
|
+
const uploading = ref(false)
|
|
11
|
+
const progress = ref(0)
|
|
12
|
+
const error = ref<string | null>(null)
|
|
13
|
+
|
|
14
|
+
const uploadToServer = async (file: File): Promise<any> => {
|
|
15
|
+
if (config.maxSize && file.size > config.maxSize) {
|
|
16
|
+
const errorMsg = `File size exceeds ${formatFileSize(config.maxSize)}`
|
|
17
|
+
error.value = errorMsg
|
|
18
|
+
config.onError?.(new Error(errorMsg))
|
|
19
|
+
throw new Error(errorMsg)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (config.allowedTypes && !config.allowedTypes.includes(file.type)) {
|
|
23
|
+
const errorMsg = 'File type not allowed'
|
|
24
|
+
error.value = errorMsg
|
|
25
|
+
config.onError?.(new Error(errorMsg))
|
|
26
|
+
throw new Error(errorMsg)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const formData = new FormData()
|
|
30
|
+
formData.append('file', file)
|
|
31
|
+
|
|
32
|
+
uploading.value = true
|
|
33
|
+
progress.value = 0
|
|
34
|
+
error.value = null
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const response = await $fetch('/api/file/upload', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: formData
|
|
40
|
+
})
|
|
41
|
+
config.onSuccess?.(response.file)
|
|
42
|
+
return response.file
|
|
43
|
+
} catch (err: any) {
|
|
44
|
+
const errorMsg = err.data?.message || 'Upload failed'
|
|
45
|
+
error.value = errorMsg
|
|
46
|
+
config.onError?.(new Error(errorMsg))
|
|
47
|
+
throw err
|
|
48
|
+
} finally {
|
|
49
|
+
uploading.value = false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const uploadMultipleFiles = async (files: FileList | File[]): Promise<any[]> => {
|
|
54
|
+
const fileArray = Array.from(files)
|
|
55
|
+
const results = []
|
|
56
|
+
|
|
57
|
+
for (const file of fileArray) {
|
|
58
|
+
try {
|
|
59
|
+
const result = await uploadToServer(file)
|
|
60
|
+
results.push(result)
|
|
61
|
+
} catch (err: any) {
|
|
62
|
+
results.push({ error: err.message })
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return results
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
uploading: readonly(uploading),
|
|
71
|
+
progress: readonly(progress),
|
|
72
|
+
error: readonly(error),
|
|
73
|
+
uploadToServer,
|
|
74
|
+
uploadMultipleFiles
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function useLivePreview() {
|
|
2
|
+
return usePreviewMode({
|
|
3
|
+
// Enable preview mode when both preview and token params exist in URL
|
|
4
|
+
shouldEnable: () => {
|
|
5
|
+
const route = useRoute();
|
|
6
|
+
return !!route.query.preview && !!route.query.token;
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
// Store the token from the URL for use in API calls
|
|
10
|
+
getState: (currentState) => {
|
|
11
|
+
const route = useRoute();
|
|
12
|
+
return {
|
|
13
|
+
token: route.query.token || currentState.token,
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// composables/useLoading.ts
|
|
2
|
+
export const useLoading = () => {
|
|
3
|
+
const loading = ref(false)
|
|
4
|
+
const loadingText = ref('')
|
|
5
|
+
|
|
6
|
+
const startLoading = (text = 'Loading...') => {
|
|
7
|
+
loading.value = true
|
|
8
|
+
loadingText.value = text
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const stopLoading = () => {
|
|
12
|
+
loading.value = false
|
|
13
|
+
loadingText.value = ''
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
loading,
|
|
18
|
+
loadingText,
|
|
19
|
+
startLoading,
|
|
20
|
+
stopLoading
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useRuntimeConfig } from 'nuxt/config';
|
|
2
|
+
|
|
3
|
+
export const getNavigation = async (id) => {
|
|
4
|
+
const config = useRuntimeConfig();
|
|
5
|
+
//const credentials = btoa(`${config.public.WP_API_EMAIL}:${config.public.WORDPRESS_TOKEN}`);
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const navigation = await $fetch(`${config.public.wordpressUrl}/wp-json/wp/v2/navigations/${id}`, {
|
|
9
|
+
headers: {
|
|
10
|
+
'Authorization': `Basic ${config.public.wordpressToken}`, // Basic Auth Header
|
|
11
|
+
'Content-Type': 'application/json'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
return navigation;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('Error fetching navigation:', error);
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { ref, onMounted } from 'vue'
|
|
2
|
+
import { useAsyncData, useNuxtApp } from '#imports'
|
|
3
|
+
|
|
4
|
+
export interface Notification {
|
|
5
|
+
id: string
|
|
6
|
+
title: string
|
|
7
|
+
content: string
|
|
8
|
+
date: string
|
|
9
|
+
type: 'order' | 'account' | 'social' | 'system'
|
|
10
|
+
isRead: boolean
|
|
11
|
+
source: 'magento' | 'directus'
|
|
12
|
+
payload?: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useNotifications() {
|
|
16
|
+
const notifications = ref<Notification[]>([])
|
|
17
|
+
const unreadCount = ref(0)
|
|
18
|
+
const { $directus, $readItems, $updateItem } = useNuxtApp() as any
|
|
19
|
+
|
|
20
|
+
// Fetch notifications from Directus
|
|
21
|
+
const fetchDirectusNotifications = async () => {
|
|
22
|
+
try {
|
|
23
|
+
const { data } = await useAsyncData<any[]>('directusNotifications', () => {
|
|
24
|
+
return $directus.request($readItems('notifications', {
|
|
25
|
+
filter: {
|
|
26
|
+
recipient: { _eq: 'current_user' }
|
|
27
|
+
},
|
|
28
|
+
sort: ['-date_created']
|
|
29
|
+
}))
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (data.value) {
|
|
33
|
+
const formattedNotifications: Notification[] = data.value.map((notification: any) => ({
|
|
34
|
+
id: String(notification.id),
|
|
35
|
+
title: String(notification.subject),
|
|
36
|
+
content: String(notification.message),
|
|
37
|
+
date: String(notification.timestamp),
|
|
38
|
+
type: (['order', 'account', 'social', 'system'].includes(notification.collection) ? notification.collection : 'system') as Notification['type'],
|
|
39
|
+
isRead: notification.status === 'read',
|
|
40
|
+
source: 'directus',
|
|
41
|
+
payload: notification.item
|
|
42
|
+
}))
|
|
43
|
+
notifications.value = [...notifications.value, ...formattedNotifications]
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Error fetching Directus notifications:', error)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fetch notifications from Magento
|
|
51
|
+
const fetchMagentoNotifications = async () => {
|
|
52
|
+
try {
|
|
53
|
+
// Replace with your Magento API endpoint
|
|
54
|
+
const response = await fetch('/api/magento/notifications')
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const text = await response.text().catch(() => '')
|
|
57
|
+
console.error('Magento notifications fetch failed', { status: response.status, body: text })
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = await response.json()
|
|
62
|
+
|
|
63
|
+
const formattedNotifications: Notification[] = data.map((notification: any) => ({
|
|
64
|
+
id: String(notification.id),
|
|
65
|
+
title: String(notification.title),
|
|
66
|
+
content: String(notification.message),
|
|
67
|
+
date: String(notification.created_at),
|
|
68
|
+
type: (['order', 'account', 'social', 'system'].includes(notification.type) ? notification.type : 'system') as Notification['type'],
|
|
69
|
+
isRead: Boolean(notification.is_read),
|
|
70
|
+
source: 'magento',
|
|
71
|
+
payload: notification.payload
|
|
72
|
+
}))
|
|
73
|
+
notifications.value = [...notifications.value, ...formattedNotifications]
|
|
74
|
+
} catch (error: any) {
|
|
75
|
+
console.error('Error fetching Magento notifications:', error?.message ?? String(error))
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Mark notification as read
|
|
80
|
+
const markAsRead = async (notificationId: string, source: 'magento' | 'directus') => {
|
|
81
|
+
try {
|
|
82
|
+
if (source === 'directus') {
|
|
83
|
+
await $directus.request($updateItem('notifications', notificationId, {
|
|
84
|
+
status: 'read'
|
|
85
|
+
}))
|
|
86
|
+
} else {
|
|
87
|
+
// Update Magento notification
|
|
88
|
+
await fetch(`/api/magento/notifications/${notificationId}/read`, {
|
|
89
|
+
method: 'POST'
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Update local state
|
|
94
|
+
const notification = notifications.value.find(n => n.id === notificationId)
|
|
95
|
+
if (notification) {
|
|
96
|
+
notification.isRead = true
|
|
97
|
+
unreadCount.value = Math.max(0, unreadCount.value - 1)
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Error marking notification as read:', error)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Mark all notifications as read
|
|
105
|
+
const markAllAsRead = async () => {
|
|
106
|
+
try {
|
|
107
|
+
// Update Directus notifications
|
|
108
|
+
await $directus.request($updateItem('notifications', {
|
|
109
|
+
filter: {
|
|
110
|
+
recipient: { _eq: 'current_user' },
|
|
111
|
+
status: { _eq: 'inbox' }
|
|
112
|
+
},
|
|
113
|
+
data: {
|
|
114
|
+
status: 'read'
|
|
115
|
+
}
|
|
116
|
+
}))
|
|
117
|
+
|
|
118
|
+
// Update Magento notifications
|
|
119
|
+
await fetch('/api/magento/notifications/read-all', {
|
|
120
|
+
method: 'POST'
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// Update local state
|
|
124
|
+
notifications.value.forEach(notification => {
|
|
125
|
+
notification.isRead = true
|
|
126
|
+
})
|
|
127
|
+
unreadCount.value = 0
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('Error marking all notifications as read:', error)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Calculate unread count
|
|
134
|
+
const updateUnreadCount = () => {
|
|
135
|
+
unreadCount.value = notifications.value.filter(n => !n.isRead).length
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
onMounted(async () => {
|
|
139
|
+
await Promise.all([
|
|
140
|
+
fetchDirectusNotifications(),
|
|
141
|
+
fetchMagentoNotifications()
|
|
142
|
+
])
|
|
143
|
+
updateUnreadCount()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
notifications,
|
|
148
|
+
unreadCount,
|
|
149
|
+
markAsRead,
|
|
150
|
+
markAllAsRead,
|
|
151
|
+
updateUnreadCount
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// composables/usePages.js
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { useRuntimeConfig } from 'nuxt/config';
|
|
4
|
+
|
|
5
|
+
export function usePages() {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
const pages = ref({});
|
|
8
|
+
|
|
9
|
+
const fetchPages = async (pageSlugsOrIds) => {
|
|
10
|
+
try {
|
|
11
|
+
const allPages = await $fetch(`${config.public.wordpressUrl}/wp-json/wp/v2/pages`, {
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
'Authorization': `Bearer ${config.public.wordpressToken}`
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
pageSlugsOrIds.forEach(slugOrId => {
|
|
19
|
+
const specificPage = allPages.find(page => page.slug === slugOrId || page.id === slugOrId);
|
|
20
|
+
|
|
21
|
+
if (specificPage) {
|
|
22
|
+
pages.value[slugOrId] = specificPage;
|
|
23
|
+
} else {
|
|
24
|
+
console.warn(`Page "${slugOrId}" not found`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Error fetching pages:', error);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
pages,
|
|
34
|
+
fetchPages,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { RichText } from '@atproto/api'
|
|
2
|
+
|
|
3
|
+
// creating richtext
|
|
4
|
+
const rt = new RichText({
|
|
5
|
+
text: 'Hello @alice.com, check out this link: https://example.com',
|
|
6
|
+
})
|
|
7
|
+
await rt.detectFacets(agent) // automatically detects mentions and links
|
|
8
|
+
const postRecord = {
|
|
9
|
+
$type: 'app.bsky.feed.post',
|
|
10
|
+
text: rt.text,
|
|
11
|
+
facets: rt.facets,
|
|
12
|
+
createdAt: new Date().toISOString(),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// rendering as markdown
|
|
16
|
+
let markdown = ''
|
|
17
|
+
for (const segment of rt.segments()) {
|
|
18
|
+
if (segment.isLink()) {
|
|
19
|
+
markdown += `[${segment.text}](${segment.link?.uri})`
|
|
20
|
+
} else if (segment.isMention()) {
|
|
21
|
+
markdown += `[${segment.text}](https://my-bsky-app.com/user/${segment.mention?.did})`
|
|
22
|
+
} else {
|
|
23
|
+
markdown += segment.text
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// calculating string lengths
|
|
28
|
+
const rt2 = new RichText({ text: 'Hello' })
|
|
29
|
+
console.log(rt2.length) // => 5
|
|
30
|
+
console.log(rt2.graphemeLength) // => 5
|
|
31
|
+
const rt3 = new RichText({ text: '๐จโ๐ฉโ๐งโ๐ง' })
|
|
32
|
+
console.log(rt3.length) // => 25
|
|
33
|
+
console.log(rt3.graphemeLength) // => 1
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createServerRootMixin } from 'vue-instantsearch/vue3/es'
|
|
2
|
+
|
|
3
|
+
export default function useServerRootMixin (indexName: string) {
|
|
4
|
+
const nuxtApp = useNuxtApp()
|
|
5
|
+
const searchClient = useMeilisearch()
|
|
6
|
+
|
|
7
|
+
const serverRootMixin = ref(createServerRootMixin({
|
|
8
|
+
searchClient,
|
|
9
|
+
indexName
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
// Install SSR mixin
|
|
13
|
+
nuxtApp.vueApp.mixin(serverRootMixin.value)
|
|
14
|
+
// TODO: ensure mixin is only installed once
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
instantsearch: serverRootMixin.value.data().instantsearch
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { apply as applyVisualEditing, setAttr } from '@directus/visual-editing';
|
|
2
|
+
import type { PrimaryKey } from '@directus/types';
|
|
3
|
+
|
|
4
|
+
interface ApplyOptions {
|
|
5
|
+
directusUrl: string;
|
|
6
|
+
elements?: HTMLElement[] | HTMLElement;
|
|
7
|
+
onSaved?: (data: { collection?: string; item?: PrimaryKey | null; payload?: Record<string, any> }) => void;
|
|
8
|
+
customClass?: string;
|
|
9
|
+
}
|
|
10
|
+
export default function useVisualEditing() {
|
|
11
|
+
// Use useState for state that persists across navigation
|
|
12
|
+
const isVisualEditingEnabled = useState('visual-editing-enabled', () => false);
|
|
13
|
+
const route = useRoute();
|
|
14
|
+
const {
|
|
15
|
+
public: { enableVisualEditing, directusUrl },
|
|
16
|
+
} = useRuntimeConfig();
|
|
17
|
+
|
|
18
|
+
// Check query param on composable initialization.
|
|
19
|
+
if (route.query['visual-editing'] === 'true' && enableVisualEditing) {
|
|
20
|
+
isVisualEditingEnabled.value = true;
|
|
21
|
+
} else if (route.query['visual-editing'] === 'false') {
|
|
22
|
+
isVisualEditingEnabled.value = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const apply = (options: Pick<ApplyOptions, 'elements' | 'onSaved' | 'customClass'>) => {
|
|
26
|
+
if (!isVisualEditingEnabled.value) return;
|
|
27
|
+
applyVisualEditing({
|
|
28
|
+
...options,
|
|
29
|
+
directusUrl,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
isVisualEditingEnabled,
|
|
35
|
+
apply,
|
|
36
|
+
setAttr,
|
|
37
|
+
};
|
|
38
|
+
}
|