@wix/agent-skills 1.17.0 → 1.18.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/package.json +1 -1
- package/skills/wix-app/SKILL.md +115 -12
- package/skills/wix-app/references/APP_MARKET_REVIEW.md +1 -1
- package/skills/wix-app/references/BACKEND_API.md +18 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +6 -0
- package/skills/wix-app/references/DASHBOARD_MODAL.md +10 -2
- package/skills/wix-app/references/DASHBOARD_PAGE.md +32 -70
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +74 -83
- package/skills/wix-app/references/SITE_PLUGIN.md +12 -0
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +74 -68
- package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +79 -0
- package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +73 -0
- package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +52 -0
- package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +184 -0
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +12 -11
- package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +1 -0
- package/skills/wix-app/references/editor-react-component/BROWSER-SUPPORT.md +56 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +16 -8
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +47 -14
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +3 -0
- package/skills/wix-base44-connector/SKILL.md +133 -44
- package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +78 -0
- package/skills/wix-base44-connector/scripts/utils.js +216 -30
- package/skills/wix-headless/entry/skill.md +1 -1
- package/skills/wix-headless/references/CAPABILITIES.md +9 -1
- package/skills/wix-headless/references/DESIGN.md +1 -0
- package/skills/wix-headless/references/DISCOVERY.md +2 -1
- package/skills/wix-headless/references/IMAGE_GENERATION.md +1 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +4 -1
- package/skills/wix-headless/references/SEED.md +2 -1
- package/skills/wix-headless/references/SETUP.md +1 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +2 -2
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +26 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +26 -10
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +8 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +254 -0
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +57 -10
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +13 -0
- package/skills/wix-headless/references/inline-recipes/setup-rentals.md +254 -0
- package/skills/wix-headless/references/non-astro.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +166 -0
- package/skills/wix-headless-fast/entry/skill.md +99 -0
- package/skills/wix-headless-fast/install/deploy.mjs +433 -0
- package/skills/wix-headless-fast/install/fast-path.mjs +141 -0
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +137 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +137 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +113 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +42 -0
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +123 -0
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +70 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +99 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +76 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +62 -0
- package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +99 -0
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +24 -0
- package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +16156 -0
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +72 -0
- package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +287 -0
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +129 -0
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +143 -0
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +119 -0
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +138 -0
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +54 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +206 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +96 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +70 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +76 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +22 -0
- package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +11962 -0
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +61 -0
- package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +302 -0
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +179 -0
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +83 -0
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +57 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +151 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +75 -0
- package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +41 -0
- package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +11262 -0
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +103 -0
- package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +324 -0
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +149 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +66 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +176 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +118 -0
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +117 -0
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +59 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +136 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +128 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +61 -0
- package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +12 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +80 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +21 -0
- package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +11884 -0
- package/skills/wix-headless-fast/references/events/seed/SEED.md +86 -0
- package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +276 -0
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +150 -0
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +317 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +269 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +231 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +135 -0
- package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +10593 -0
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +108 -0
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +320 -0
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +63 -0
- package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +60 -0
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +155 -0
- package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +78 -0
- package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +47 -0
- package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +66 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +88 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +53 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +122 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +50 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +21 -0
- package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +42 -0
- package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +10 -0
- package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +11 -0
- package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +10329 -0
- package/skills/wix-headless-fast/references/members/seed/SEED.md +39 -0
- package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +94 -0
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +134 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +110 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +90 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +115 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +59 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +39 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +58 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +165 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +55 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +30 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +21 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +29 -0
- package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +10197 -0
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +78 -0
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +258 -0
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +133 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +58 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +126 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +37 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +39 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +75 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +45 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +30 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +41 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +40 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +21 -0
- package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +11226 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +71 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +279 -0
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +165 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +142 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +26 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +121 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +155 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +55 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +43 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +177 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +165 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +103 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +254 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +146 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +168 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +47 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +22 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +12 -0
- package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +12560 -0
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +81 -0
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +479 -0
- package/skills/wix-headless-fast/references/shared/CONTENT.md +22 -0
- package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +73 -0
- package/skills/wix-headless-fast/references/shared/DESIGN.md +26 -0
- package/skills/wix-headless-fast/references/shared/app/styles/global.css +52 -0
- package/skills/wix-headless-fast/references/shared/app/wix/config.ts +15 -0
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +27 -0
- package/skills/wix-headless-fast/references/shared/app/wix/money.ts +24 -0
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +76 -0
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +46 -0
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +46 -0
- package/skills/wix-headless-fast/references/shared/seed/images.mjs +145 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +149 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +26 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +121 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +100 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +130 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +48 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +66 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +103 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +48 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +140 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +80 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +101 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +221 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +215 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +109 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +46 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +56 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +23 -0
- package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +12247 -0
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +85 -0
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +415 -0
- package/skills/wix-manage/SKILL.md +113 -89
- package/skills/wix-manage/references/dashboard-navigation/routes.json +12 -52
- package/skills/wix-manage/references/ecommerce/shipping/ecom-shipping-api.md +0 -2
- package/skills/wix-manage/references/forms/create-form.md +272 -184
- package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +13 -10
- package/skills/wix-manage/references/forms/update-form.md +122 -0
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +141 -0
- package/skills/wix-manage/references/google-business-profile/connect-google-business-profile.md +134 -0
- package/skills/wix-manage/references/google-business-profile/manage-google-business-profile-locations.md +182 -0
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +3 -0
- package/skills/wix-manage/references/media/generate-image-with-ai.md +86 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +37 -7
- package/skills/wix-manage/references/stores/update-product-with-options.md +83 -0
- package/skills/wix-vibe-headless/SKILL.md +18 -4
- package/skills/wix-vibe-headless/install/deploy.cjs +12 -4
- package/skills/wix-vibe-headless/install/pin-agents-md.cjs +2 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44.md +27 -61
- package/skills/wix-vibe-headless/platforms/generic.md +6 -3
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +67 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +128 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +5 -3
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +250 -0
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +12 -5
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +41 -0
- package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.js +267 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +6 -3
- package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +486 -0
- package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +425 -0
- package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +187 -0
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +238 -0
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +108 -0
- package/skills/wix-vibe-headless/references/forms/seed/SEED.md +1836 -0
- package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +13 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +137 -84
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +15 -8
- package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +37 -0
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +18 -6
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +30 -7
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +46 -10
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +0 -110
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +0 -51
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +0 -49
- package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +0 -92
- package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +0 -139
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Collections gallery. SSR-friendly: pass server-fetched data as `initialCollections`
|
|
2
|
+
// (Astro frontmatter / server component) and no client fetch happens; a SPA passes nothing.
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { fetchCollections } from "../../wix/portfolio/portfolio";
|
|
5
|
+
import type { CollectionSummary } from "../../wix/portfolio/types";
|
|
6
|
+
|
|
7
|
+
export interface UseCollectionsOptions {
|
|
8
|
+
initialCollections?: CollectionSummary[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UseCollections {
|
|
12
|
+
/** null while the first load is in flight — render skeletons, not an empty state. */
|
|
13
|
+
collections: CollectionSummary[] | null;
|
|
14
|
+
error: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useCollections({ initialCollections }: UseCollectionsOptions = {}): UseCollections {
|
|
18
|
+
const [collections, setCollections] = useState<CollectionSummary[] | null>(initialCollections ?? null);
|
|
19
|
+
const [error, setError] = useState<string | null>(null);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let alive = true;
|
|
23
|
+
if (!initialCollections) {
|
|
24
|
+
fetchCollections()
|
|
25
|
+
.then((c) => alive && setCollections(c))
|
|
26
|
+
.catch((e) => {
|
|
27
|
+
if (!alive) return;
|
|
28
|
+
setCollections([]);
|
|
29
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return () => {
|
|
33
|
+
alive = false;
|
|
34
|
+
};
|
|
35
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
return { collections, error };
|
|
39
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Project detail + media gallery, by slug. SSR-friendly: pass both `initial*` and no client
|
|
2
|
+
// fetch happens. `notFound` is the real not-found signal (project stays null while loading).
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { fetchProjectBySlug, fetchProjectGallery } from "../../wix/portfolio/portfolio";
|
|
5
|
+
import type { GalleryItem, ProjectDetail } from "../../wix/portfolio/types";
|
|
6
|
+
|
|
7
|
+
export interface UseProjectDetailOptions {
|
|
8
|
+
initialProject?: ProjectDetail;
|
|
9
|
+
initialItems?: GalleryItem[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface UseProjectDetail {
|
|
13
|
+
/** null while loading AND when not found — check `notFound` to tell them apart. */
|
|
14
|
+
project: ProjectDetail | null;
|
|
15
|
+
notFound: boolean;
|
|
16
|
+
/** null while the gallery load is in flight — render skeletons, not an empty state. */
|
|
17
|
+
items: GalleryItem[] | null;
|
|
18
|
+
error: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useProjectDetail(
|
|
22
|
+
slug: string,
|
|
23
|
+
{ initialProject, initialItems }: UseProjectDetailOptions = {},
|
|
24
|
+
): UseProjectDetail {
|
|
25
|
+
const [project, setProject] = useState<ProjectDetail | null>(initialProject ?? null);
|
|
26
|
+
const [notFound, setNotFound] = useState(false);
|
|
27
|
+
const [items, setItems] = useState<GalleryItem[] | null>(initialItems ?? null);
|
|
28
|
+
const [error, setError] = useState<string | null>(null);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (initialProject && initialItems) return;
|
|
32
|
+
let alive = true;
|
|
33
|
+
(async () => {
|
|
34
|
+
try {
|
|
35
|
+
const proj = initialProject ?? (await fetchProjectBySlug(slug));
|
|
36
|
+
if (!alive) return;
|
|
37
|
+
if (!proj) {
|
|
38
|
+
setNotFound(true);
|
|
39
|
+
setItems([]);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setProject(proj);
|
|
43
|
+
const gallery = await fetchProjectGallery(proj.id);
|
|
44
|
+
if (alive) setItems(gallery);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (!alive) return;
|
|
47
|
+
setItems([]);
|
|
48
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
return () => {
|
|
52
|
+
alive = false;
|
|
53
|
+
};
|
|
54
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
|
+
}, [slug]);
|
|
56
|
+
|
|
57
|
+
return { project, notFound, items, error };
|
|
58
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Portfolio reads (Wix Portfolio v1) — the only file that touches raw portfolio entities.
|
|
2
|
+
// Read-only vertical: no cart, no checkout, no @wix/ecom. Everything it returns is a plain
|
|
3
|
+
// DTO from ./types. Copy as-is; extend by adding functions, not by editing these.
|
|
4
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/portfolio/collections/list-collections.md
|
|
5
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/portfolio/projects/list-projects.md
|
|
6
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/portfolio/project-items/list-project-items.md
|
|
7
|
+
// docs: https://dev.wix.com/docs/sdk/core-modules/sdk/media.md
|
|
8
|
+
import {
|
|
9
|
+
collections as collectionsModule,
|
|
10
|
+
projects as projectsModule,
|
|
11
|
+
projectItems as projectItemsModule,
|
|
12
|
+
} from "@wix/portfolio";
|
|
13
|
+
import { media, VideoResolution } from "@wix/sdk";
|
|
14
|
+
import { wixModule } from "../sdk";
|
|
15
|
+
import { imgSrc } from "../media";
|
|
16
|
+
import type {
|
|
17
|
+
CollectionSummary,
|
|
18
|
+
GalleryItem,
|
|
19
|
+
ProjectDetail,
|
|
20
|
+
ProjectDetailRow,
|
|
21
|
+
ProjectSummary,
|
|
22
|
+
} from "./types";
|
|
23
|
+
|
|
24
|
+
const collections = wixModule(collectionsModule);
|
|
25
|
+
const projects = wixModule(projectsModule);
|
|
26
|
+
const projectItems = wixModule(projectItemsModule);
|
|
27
|
+
|
|
28
|
+
type Raw = Record<string, any>;
|
|
29
|
+
|
|
30
|
+
// The SDK types coverImage.imageInfo / item.image.imageInfo as a bare wix:image:// STRING
|
|
31
|
+
// (the REST docs show an object with .url — the package wins); imgSrc resolves either shape.
|
|
32
|
+
|
|
33
|
+
// videoInfo is likewise a bare wix:video:// string; getVideoUrl derives the mp4 rendition +
|
|
34
|
+
// poster (a wix:image:// string → imgSrc again). 720p is the standard transcode. Tolerates
|
|
35
|
+
// the REST-shaped object ({ resolutions, posters }) in case a surface returns it at runtime.
|
|
36
|
+
function toVideo(value: unknown): { videoUrl: string | null; posterUrl: string } {
|
|
37
|
+
if (typeof value === "string" && value.startsWith("wix:video://")) {
|
|
38
|
+
try {
|
|
39
|
+
const v = media.getVideoUrl(value, VideoResolution.MID);
|
|
40
|
+
return { videoUrl: v.url, posterUrl: imgSrc(v.thumbnail, 1200, 675) };
|
|
41
|
+
} catch {
|
|
42
|
+
return { videoUrl: null, posterUrl: "" };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const raw = value as Raw | null | undefined;
|
|
46
|
+
const url = raw?.resolutions?.[0]?.url;
|
|
47
|
+
return {
|
|
48
|
+
videoUrl: typeof url === "string" ? url : null,
|
|
49
|
+
posterUrl: imgSrc(raw?.posters?.[0], 1200, 675),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toCollection(raw: Raw): CollectionSummary {
|
|
54
|
+
return {
|
|
55
|
+
id: raw._id ?? "",
|
|
56
|
+
slug: raw.slug ?? "",
|
|
57
|
+
title: raw.title ?? "",
|
|
58
|
+
description: raw.description ?? "",
|
|
59
|
+
imageUrl: imgSrc(raw.coverImage?.imageInfo, 1200, 900),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// coverImage is ONE-OF with coverVideo — a video-covered project falls back to its poster.
|
|
64
|
+
function projectCover(raw: Raw): string {
|
|
65
|
+
return imgSrc(raw.coverImage?.imageInfo, 1200, 900) || toVideo(raw.coverVideo?.videoInfo).posterUrl;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function toProject(raw: Raw): ProjectSummary {
|
|
69
|
+
return {
|
|
70
|
+
id: raw._id ?? "",
|
|
71
|
+
slug: raw.slug ?? "",
|
|
72
|
+
title: raw.title ?? "",
|
|
73
|
+
description: raw.description ?? "",
|
|
74
|
+
imageUrl: projectCover(raw),
|
|
75
|
+
collectionIds: raw.collectionIds ?? [],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// A details row is a one-of: { label, text } or { label, link: { text, url, target } }.
|
|
80
|
+
function toDetailRow(raw: Raw): ProjectDetailRow {
|
|
81
|
+
return {
|
|
82
|
+
label: raw.label ?? "",
|
|
83
|
+
text: raw.link?.text ?? raw.text ?? "",
|
|
84
|
+
url: raw.link?.url ?? null,
|
|
85
|
+
target: raw.link?.target ?? null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function toDetail(raw: Raw): ProjectDetail {
|
|
90
|
+
return { ...toProject(raw), details: (raw.details ?? []).map((d: Raw) => toDetailRow(d)) };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function toGalleryItem(raw: Raw): GalleryItem | null {
|
|
94
|
+
const base = {
|
|
95
|
+
id: raw._id ?? "",
|
|
96
|
+
title: raw.title ?? "",
|
|
97
|
+
description: raw.description ?? "",
|
|
98
|
+
linkUrl: raw.link?.url ?? null,
|
|
99
|
+
linkTarget: raw.link?.target ?? null,
|
|
100
|
+
};
|
|
101
|
+
if (raw.type === "IMAGE") {
|
|
102
|
+
const imageUrl = imgSrc(raw.image?.imageInfo, 1600, 1200);
|
|
103
|
+
return imageUrl ? { ...base, kind: "image", imageUrl, videoUrl: null } : null;
|
|
104
|
+
}
|
|
105
|
+
if (raw.type === "VIDEO") {
|
|
106
|
+
const { videoUrl, posterUrl } = toVideo(raw.video?.videoInfo);
|
|
107
|
+
return videoUrl ? { ...base, kind: "video", imageUrl: posterUrl, videoUrl } : null;
|
|
108
|
+
}
|
|
109
|
+
return null; // UNDEFINED — nothing renderable
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* List visible collections in the owner's dashboard order (sortOrder — collections have one,
|
|
114
|
+
* projects don't). An omitted `hidden` comes back ABSENT (proto3): test falsy, never === false.
|
|
115
|
+
*/
|
|
116
|
+
export async function fetchCollections({ limit = 100 } = {}): Promise<CollectionSummary[]> {
|
|
117
|
+
const res = await collections.listCollections({ paging: { limit } });
|
|
118
|
+
return (res.collections ?? [])
|
|
119
|
+
.filter((c: Raw) => !c.hidden)
|
|
120
|
+
.sort((a: Raw, b: Raw) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
|
|
121
|
+
.map((c: Raw) => toCollection(c));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Fetch one visible collection by its URL slug. Null when not found or hidden. */
|
|
125
|
+
export async function fetchCollectionBySlug(slug: string): Promise<CollectionSummary | null> {
|
|
126
|
+
const res = await collections.queryCollections().eq("slug", slug).limit(1).find();
|
|
127
|
+
const raw = res.items?.[0] as Raw | undefined;
|
|
128
|
+
return raw && !raw.hidden ? toCollection(raw) : null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* List visible projects (list order — Project exposes no sortOrder), optionally only one
|
|
133
|
+
* collection's. The collection filter is client-side over the one list call (portfolios are
|
|
134
|
+
* small; a project links to collections via collectionIds).
|
|
135
|
+
*/
|
|
136
|
+
export async function fetchProjects({
|
|
137
|
+
collectionId,
|
|
138
|
+
limit = 100,
|
|
139
|
+
}: { collectionId?: string; limit?: number } = {}): Promise<ProjectSummary[]> {
|
|
140
|
+
const res = await projects.listProjects({ paging: { limit } });
|
|
141
|
+
return (res.projects ?? [])
|
|
142
|
+
.filter((p: Raw) => !p.hidden)
|
|
143
|
+
.filter((p: Raw) => !collectionId || (p.collectionIds ?? []).includes(collectionId))
|
|
144
|
+
.map((p: Raw) => toProject(p));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Fetch one visible project by its URL slug. Null when not found or hidden. */
|
|
148
|
+
export async function fetchProjectBySlug(slug: string): Promise<ProjectDetail | null> {
|
|
149
|
+
const res = await projects.queryProjects().eq("slug", slug).limit(1).find();
|
|
150
|
+
const raw = res.items?.[0] as Raw | undefined;
|
|
151
|
+
return raw && !raw.hidden ? toDetail(raw) : null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* A project's media gallery in dashboard order (item sortOrder). IMAGE and VIDEO items only —
|
|
156
|
+
* UNDEFINED and unresolvable media are dropped. First arg is the project _id (positional).
|
|
157
|
+
*/
|
|
158
|
+
export async function fetchProjectGallery(projectId: string): Promise<GalleryItem[]> {
|
|
159
|
+
const res = await projectItems.listProjectItems(projectId, { paging: { limit: 100 } });
|
|
160
|
+
return (res.items ?? [])
|
|
161
|
+
.slice()
|
|
162
|
+
.sort((a: Raw, b: Raw) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
|
|
163
|
+
.map((it: Raw) => toGalleryItem(it))
|
|
164
|
+
.filter((g: GalleryItem | null): g is GalleryItem => g !== null);
|
|
165
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Portfolio DTOs — the serializable shapes every hook, component, and page consumes.
|
|
2
|
+
// Plain JSON: safe as Astro island props or across server/client boundaries. Images are
|
|
3
|
+
// resolved https URLs; videos are playable mp4 URLs with a poster frame.
|
|
4
|
+
|
|
5
|
+
/** A collection as the gallery grid needs it. */
|
|
6
|
+
export interface CollectionSummary {
|
|
7
|
+
id: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
/** Resolved https URL ("" when the collection has no cover). */
|
|
12
|
+
imageUrl: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** A project as a grid tile needs it. */
|
|
16
|
+
export interface ProjectSummary {
|
|
17
|
+
id: string;
|
|
18
|
+
slug: string;
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
/** Cover image; a video-covered project falls back to its poster frame ("" when neither). */
|
|
22
|
+
imageUrl: string;
|
|
23
|
+
/** The collections this project belongs to (a project can be in several). */
|
|
24
|
+
collectionIds: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** One row of a project's metadata (Role, Year, Client…): plain text or an outbound link. */
|
|
28
|
+
export interface ProjectDetailRow {
|
|
29
|
+
label: string;
|
|
30
|
+
text: string;
|
|
31
|
+
/** Non-null → render the row's text as a link. */
|
|
32
|
+
url: string | null;
|
|
33
|
+
/** Link rows only: "_blank" | "_self". */
|
|
34
|
+
target: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A project as the detail page needs it. */
|
|
38
|
+
export interface ProjectDetail extends ProjectSummary {
|
|
39
|
+
details: ProjectDetailRow[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One media item of a project's gallery (dashboard order). */
|
|
43
|
+
export interface GalleryItem {
|
|
44
|
+
id: string;
|
|
45
|
+
kind: "image" | "video";
|
|
46
|
+
title: string;
|
|
47
|
+
description: string;
|
|
48
|
+
/** image: the resolved image. video: the poster frame ("" when the video has none). */
|
|
49
|
+
imageUrl: string;
|
|
50
|
+
/** video only: playable mp4 URL. */
|
|
51
|
+
videoUrl: string | null;
|
|
52
|
+
/** Optional owner-attached outbound link for this item. */
|
|
53
|
+
linkUrl: string | null;
|
|
54
|
+
linkTarget: string | null;
|
|
55
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
// The site chrome for every portfolio page — THIS file (header/footer/nav) plus the @theme
|
|
3
|
+
// token block in src/styles/global.css are what you brand. Keep the <slot name="seo-tags" />
|
|
4
|
+
// in <head> and the global.css import. (Deploy is fill-only: if another vertical is already
|
|
5
|
+
// deployed, its SiteLayout won — merge a Portfolio nav link there instead.)
|
|
6
|
+
import "../styles/global.css";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
}
|
|
12
|
+
const { title, description } = Astro.props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<!doctype html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8" />
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
20
|
+
<title>{title}</title>
|
|
21
|
+
{description && <meta name="description" content={description} />}
|
|
22
|
+
<slot name="seo-tags" />
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<header class="sticky top-0 z-40 border-b border-border bg-background/80 backdrop-blur">
|
|
26
|
+
<div class="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
|
|
27
|
+
<a href="/" class="text-base font-bold tracking-tight text-foreground no-underline">Brand Name</a>
|
|
28
|
+
<nav class="flex items-center gap-6 text-sm">
|
|
29
|
+
<a href="/portfolio" class="text-foreground no-underline transition-colors hover:text-muted-foreground">Portfolio</a>
|
|
30
|
+
</nav>
|
|
31
|
+
</div>
|
|
32
|
+
</header>
|
|
33
|
+
<main class="mx-auto max-w-6xl px-6 pb-24 pt-10">
|
|
34
|
+
<slot />
|
|
35
|
+
</main>
|
|
36
|
+
<footer class="border-t border-border px-6 py-10 text-center text-sm text-muted-foreground">
|
|
37
|
+
© Brand Name
|
|
38
|
+
</footer>
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Collection page — header + its projects, resolved by slug SERVER-SIDE.
|
|
3
|
+
// Portfolio has no documented Wix SEO item type, so this page carries plain
|
|
4
|
+
// <title>/<meta description> from the DTO (no wixMetadata / <SEO.Tags> — unlike
|
|
5
|
+
// storefront/bookings item pages). Keep the frontmatter; swap the island import to YOUR
|
|
6
|
+
// collection-page component.
|
|
7
|
+
import SiteLayout from "../../layouts/SiteLayout.astro";
|
|
8
|
+
import CollectionProjectsView from "../../components/portfolio/CollectionProjectsView";
|
|
9
|
+
import { fetchCollectionBySlug, fetchProjects } from "../../wix/portfolio/portfolio";
|
|
10
|
+
import type { CollectionSummary, ProjectSummary } from "../../wix/portfolio/types";
|
|
11
|
+
|
|
12
|
+
const slug = Astro.params.slug!;
|
|
13
|
+
|
|
14
|
+
let collection: CollectionSummary | null = null;
|
|
15
|
+
let projects: ProjectSummary[] = [];
|
|
16
|
+
try {
|
|
17
|
+
collection = await fetchCollectionBySlug(slug);
|
|
18
|
+
if (collection) projects = await fetchProjects({ collectionId: collection.id });
|
|
19
|
+
} catch {
|
|
20
|
+
// Guarded: an unhandled SSR throw truncates the response mid-stream.
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!collection) {
|
|
24
|
+
return new Response(null, { status: 404 });
|
|
25
|
+
}
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<SiteLayout title={collection.title} description={collection.description}>
|
|
29
|
+
<CollectionProjectsView client:load slug={slug} initialCollection={collection} initialProjects={projects} />
|
|
30
|
+
</SiteLayout>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Collections gallery — data fetched SERVER-SIDE (SEO) and handed to the island as DTO props.
|
|
3
|
+
// Keep this frontmatter; swap the island import to YOUR gallery component.
|
|
4
|
+
import SiteLayout from "../layouts/SiteLayout.astro";
|
|
5
|
+
import CollectionsView from "../components/portfolio/CollectionsView";
|
|
6
|
+
import { fetchCollections } from "../wix/portfolio/portfolio";
|
|
7
|
+
import type { CollectionSummary } from "../wix/portfolio/types";
|
|
8
|
+
|
|
9
|
+
let collections: CollectionSummary[] = [];
|
|
10
|
+
try {
|
|
11
|
+
collections = await fetchCollections();
|
|
12
|
+
} catch {
|
|
13
|
+
// An unguarded SSR throw truncates the response mid-stream; the island renders the
|
|
14
|
+
// empty state instead.
|
|
15
|
+
}
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<SiteLayout title="Portfolio">
|
|
19
|
+
<h1 class="mb-8 text-2xl font-semibold tracking-tight">Portfolio</h1>
|
|
20
|
+
<CollectionsView client:load initialCollections={collections} />
|
|
21
|
+
</SiteLayout>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Project detail page — project + media gallery, resolved by slug SERVER-SIDE.
|
|
3
|
+
// Portfolio has no documented Wix SEO item type, so this page carries plain
|
|
4
|
+
// <title>/<meta description> from the DTO (no wixMetadata / <SEO.Tags>). Keep the
|
|
5
|
+
// frontmatter; swap the island import to YOUR detail component.
|
|
6
|
+
import SiteLayout from "../../layouts/SiteLayout.astro";
|
|
7
|
+
import ProjectDetailView from "../../components/portfolio/ProjectDetailView";
|
|
8
|
+
import { fetchProjectBySlug, fetchProjectGallery } from "../../wix/portfolio/portfolio";
|
|
9
|
+
import type { GalleryItem, ProjectDetail } from "../../wix/portfolio/types";
|
|
10
|
+
|
|
11
|
+
const slug = Astro.params.slug!;
|
|
12
|
+
|
|
13
|
+
let project: ProjectDetail | null = null;
|
|
14
|
+
let items: GalleryItem[] = [];
|
|
15
|
+
try {
|
|
16
|
+
project = await fetchProjectBySlug(slug);
|
|
17
|
+
if (project) items = await fetchProjectGallery(project.id);
|
|
18
|
+
} catch {
|
|
19
|
+
// Guarded: an unhandled SSR throw truncates the response mid-stream.
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!project) {
|
|
23
|
+
return new Response(null, { status: 404 });
|
|
24
|
+
}
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<SiteLayout title={project.title} description={project.description}>
|
|
28
|
+
<ProjectDetailView client:load slug={slug} initialProject={project} initialItems={items} />
|
|
29
|
+
</SiteLayout>
|