@wix/agent-skills 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/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +29 -0
- package/skills/wix-app/SKILL.md +344 -0
- package/skills/wix-app/references/APP_IDENTIFIERS.md +35 -0
- package/skills/wix-app/references/APP_MARKET_REVIEW.md +311 -0
- package/skills/wix-app/references/APP_VALIDATION.md +171 -0
- package/skills/wix-app/references/BACKEND_API.md +233 -0
- package/skills/wix-app/references/BACKEND_EVENT.md +116 -0
- package/skills/wix-app/references/CODE_QUALITY.md +33 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +587 -0
- package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +157 -0
- package/skills/wix-app/references/DASHBOARD_MODAL.md +283 -0
- package/skills/wix-app/references/DASHBOARD_PAGE.md +403 -0
- package/skills/wix-app/references/DASHBOARD_PLUGIN.md +205 -0
- package/skills/wix-app/references/DATA_COLLECTION.md +451 -0
- package/skills/wix-app/references/DOCUMENTATION.md +42 -0
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +65 -0
- package/skills/wix-app/references/EMBEDDED_SCRIPT.md +468 -0
- package/skills/wix-app/references/EXTENSION_REGISTRATION.md +96 -0
- package/skills/wix-app/references/SERVICE_PLUGIN.md +235 -0
- package/skills/wix-app/references/SITE_PLUGIN.md +436 -0
- package/skills/wix-app/references/STORES_VERSIONING.md +334 -0
- package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +84 -0
- package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +345 -0
- package/skills/wix-app/references/dashboard-menu-plugin/blog-slots.md +71 -0
- package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +58 -0
- package/skills/wix-app/references/dashboard-menu-plugin/crm-slots.md +8 -0
- package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +17 -0
- package/skills/wix-app/references/dashboard-menu-plugin/events-slots.md +29 -0
- package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +27 -0
- package/skills/wix-app/references/dashboard-menu-plugin/stores-slots.md +35 -0
- package/skills/wix-app/references/dashboard-page/API_SPEC.md +138 -0
- package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +573 -0
- package/skills/wix-app/references/dashboard-page/DYNAMIC_PARAMETERS.md +269 -0
- package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +164 -0
- package/skills/wix-app/references/dashboard-plugin/SLOTS.md +294 -0
- package/skills/wix-app/references/data-collection/WIX_DATA.md +270 -0
- package/skills/wix-app/references/data-collection/extension-template.ts +107 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +138 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +454 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +165 -0
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +576 -0
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +170 -0
- package/skills/wix-app/references/editor-react-component/EDIT-FLOW.md +6 -0
- package/skills/wix-app/references/editor-react-component/PARTS.md +25 -0
- package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +203 -0
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +167 -0
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +137 -0
- package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +112 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +118 -0
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +87 -0
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +86 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +135 -0
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +79 -0
- package/skills/wix-app/references/service-plugin/VALIDATIONS.md +68 -0
- package/skills/wix-app/references/site-plugin/EXAMPLES.md +311 -0
- package/skills/wix-app/references/site-plugin/SLOTS.md +263 -0
- package/skills/wix-design-system/SKILL.md +129 -0
- package/skills/wix-design-system/references/file-structure.md +56 -0
- package/skills/wix-design-system/scripts/wds.cjs +531 -0
- package/skills/wix-headless/SKILL.md +418 -0
- package/skills/wix-headless/references/DISCOVERY.md +332 -0
- package/skills/wix-headless/references/ORCHESTRATION.md +269 -0
- package/skills/wix-headless/references/SETUP.md +154 -0
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +152 -0
- package/skills/wix-headless/references/blog/BLOG_PAGES.md +299 -0
- package/skills/wix-headless/references/blog/BLOG_SETUP.md +218 -0
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +32 -0
- package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +373 -0
- package/skills/wix-headless/references/cms/FAQ_KNOWLEDGE_BASE.md +229 -0
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +40 -0
- package/skills/wix-headless/references/cms/PORTFOLIO.md +454 -0
- package/skills/wix-headless/references/cms/RESOURCE_LIBRARY.md +356 -0
- package/skills/wix-headless/references/cms/TEAM_DIRECTORY.md +299 -0
- package/skills/wix-headless/references/commands/install-app.md +42 -0
- package/skills/wix-headless/references/commands/known-apps.json +5 -0
- package/skills/wix-headless/references/commands/mcp-bootstrap.md +28 -0
- package/skills/wix-headless/references/designer/INSTRUCTIONS.md +828 -0
- package/skills/wix-headless/references/ecom/CART_PAGES.md +120 -0
- package/skills/wix-headless/references/ecom/CART_WIRING.md +103 -0
- package/skills/wix-headless/references/ecom/COMPONENTS_CSS.md +132 -0
- package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +59 -0
- package/skills/wix-headless/references/forms/CONTACT_FORM.md +477 -0
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +43 -0
- package/skills/wix-headless/references/gift-cards/COMPONENTS.md +119 -0
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +51 -0
- package/skills/wix-headless/references/gift-cards/PAGES.md +129 -0
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +359 -0
- package/skills/wix-headless/references/shared/BUILD_NOISE.md +8 -0
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +185 -0
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +155 -0
- package/skills/wix-headless/references/shared/MCP_PREFIX.md +105 -0
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +97 -0
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +423 -0
- package/skills/wix-headless/references/shared/STYLING.md +117 -0
- package/skills/wix-headless/references/stores/BACK_IN_STOCK.md +178 -0
- package/skills/wix-headless/references/stores/CATEGORY_PAGES.md +86 -0
- package/skills/wix-headless/references/stores/COMPONENTS_CSS.md +129 -0
- package/skills/wix-headless/references/stores/HOME_AND_NAV.md +227 -0
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +96 -0
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +249 -0
- package/skills/wix-headless/references/stores/PRODUCT_PAGES.md +213 -0
- package/skills/wix-headless/references/stores/SHARED_WIRING.md +164 -0
- package/skills/wix-headless/references/verticals/_schema.json +160 -0
- package/skills/wix-headless/references/verticals/_schema.md +185 -0
- package/skills/wix-headless/references/verticals/blog.md +125 -0
- package/skills/wix-headless/references/verticals/cms.md +121 -0
- package/skills/wix-headless/references/verticals/ecom.md +96 -0
- package/skills/wix-headless/references/verticals/forms.md +106 -0
- package/skills/wix-headless/references/verticals/gift-cards.md +121 -0
- package/skills/wix-headless/references/verticals/stores.md +167 -0
- package/skills/wix-headless/scripts/check-manifest.mjs +198 -0
- package/skills/wix-headless/scripts/copy-utility-templates.mjs +153 -0
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +77 -0
- package/skills/wix-headless/scripts/finalize-run-json.mjs +141 -0
- package/skills/wix-headless/scripts/init-site-json.mjs +63 -0
- package/skills/wix-headless/scripts/patch-decorative-slots.mjs +124 -0
- package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +190 -0
- package/skills/wix-headless/scripts/preview.sh +45 -0
- package/skills/wix-headless/scripts/release.sh +73 -0
- package/skills/wix-headless/scripts/scaffold.sh +53 -0
- package/skills/wix-headless/scripts/seed-utilities.sh +55 -0
- package/skills/wix-headless/shared-utilities/analytics.ts +40 -0
- package/skills/wix-headless/shared-utilities/ricos.ts +179 -0
- package/skills/wix-headless/shared-utilities/wix-image.ts +72 -0
- package/skills/wix-headless/templates/ecom/CartBadge.tsx +84 -0
- package/skills/wix-headless/templates/ecom/CartView.tsx +543 -0
- package/skills/wix-headless/templates/ecom/cart.astro +13 -0
- package/skills/wix-headless/templates/ecom/components-ecom.css +215 -0
- package/skills/wix-headless/templates/ecom/discounts.ts +71 -0
- package/skills/wix-headless/templates/ecom/thank-you.astro +24 -0
- package/skills/wix-headless/templates/gift-cards/GiftCardPurchase.tsx +172 -0
- package/skills/wix-headless/templates/gift-cards/_home-teaser-snippet.astro +42 -0
- package/skills/wix-headless/templates/gift-cards/_nav-snippet.astro +11 -0
- package/skills/wix-headless/templates/gift-cards/components-gift-cards.css +219 -0
- package/skills/wix-headless/templates/gift-cards/gift-cards.astro +51 -0
- package/skills/wix-headless/templates/gift-cards/gift-cards.ts +75 -0
- package/skills/wix-headless/templates/stores/AddToCartButton.tsx +77 -0
- package/skills/wix-headless/templates/stores/BackInStockForm.tsx +176 -0
- package/skills/wix-headless/templates/stores/CategoryRail.astro +69 -0
- package/skills/wix-headless/templates/stores/ProductCard.astro +79 -0
- package/skills/wix-headless/templates/stores/ProductPurchase.tsx +397 -0
- package/skills/wix-headless/templates/stores/SeoTags.astro +21 -0
- package/skills/wix-headless/templates/stores/back-in-stock.ts +67 -0
- package/skills/wix-headless/templates/stores/categories.ts +155 -0
- package/skills/wix-headless/templates/stores/category/[slug].astro +130 -0
- package/skills/wix-headless/templates/stores/components-stores.css +295 -0
- package/skills/wix-headless/templates/stores/products/[slug].astro +128 -0
- package/skills/wix-headless/templates/stores/products/index.astro +101 -0
- package/skills/wix-manage/SKILL.md +280 -0
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +155 -0
- package/skills/wix-manage/references/app-installation/list-installed-apps.md +124 -0
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +226 -0
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +95 -0
- package/skills/wix-manage/references/bookings/booking-system-integration-gaps.md +416 -0
- package/skills/wix-manage/references/bookings/bookings-staff-setup.md +255 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +462 -0
- package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +278 -0
- package/skills/wix-manage/references/bookings/external-calendar-integration.md +258 -0
- package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +231 -0
- package/skills/wix-manage/references/calendar/configure-default-business-hours.md +226 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +422 -0
- package/skills/wix-manage/references/cms/cms-data-operations-extended.md +263 -0
- package/skills/wix-manage/references/cms/cms-ecommerce-catalog-integration.md +136 -0
- package/skills/wix-manage/references/cms/cms-references-and-relationships.md +160 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +143 -0
- package/skills/wix-manage/references/contacts/bulk-delete-contacts.md +73 -0
- package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +67 -0
- package/skills/wix-manage/references/domains/domain-search-and-purchase.md +434 -0
- package/skills/wix-manage/references/ecommerce/api-recommendation-tracking.md +420 -0
- package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +267 -0
- package/skills/wix-manage/references/ecommerce/flow-bundle-and-save.md +290 -0
- package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +300 -0
- package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +364 -0
- package/skills/wix-manage/references/ecommerce/flow-seasonal-promotion.md +315 -0
- package/skills/wix-manage/references/ecommerce/flow-stock-mover.md +308 -0
- package/skills/wix-manage/references/ecommerce/flow-upsell-boost.md +271 -0
- package/skills/wix-manage/references/ecommerce/goal-clear-inventory.md +118 -0
- package/skills/wix-manage/references/ecommerce/goal-drive-cross-sells.md +122 -0
- package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +132 -0
- package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +148 -0
- package/skills/wix-manage/references/ecommerce/goal-seasonal-revenue.md +146 -0
- package/skills/wix-manage/references/ecommerce/guardrail-discount-conflicts.md +118 -0
- package/skills/wix-manage/references/ecommerce/guardrail-margin-protection.md +88 -0
- package/skills/wix-manage/references/ecommerce/guardrail-rate-pricing-sanity.md +108 -0
- package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +124 -0
- package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +299 -0
- package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +582 -0
- package/skills/wix-manage/references/ecommerce/setup-coupons.md +338 -0
- package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +444 -0
- package/skills/wix-manage/references/ecommerce/setup-shipping-rates.md +86 -0
- package/skills/wix-manage/references/ecommerce/setup-shipping-regions.md +56 -0
- package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +209 -0
- package/skills/wix-manage/references/ecommerce/skill-graph.md +139 -0
- package/skills/wix-manage/references/ecommerce/troubleshoot-checkout-delivery-dropoff.md +124 -0
- package/skills/wix-manage/references/ecommerce/troubleshoot-discount-not-applying.md +95 -0
- package/skills/wix-manage/references/events/list-events.md +203 -0
- package/skills/wix-manage/references/forms/create-form.md +260 -0
- package/skills/wix-manage/references/get-paid/create-payment-links.md +212 -0
- package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +57 -0
- package/skills/wix-manage/references/get-paid/payment-links-for-bookings.md +101 -0
- package/skills/wix-manage/references/media/upload-media-to-wix.md +216 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +77 -0
- package/skills/wix-manage/references/pricing-plans/pricing-plans-bookings-integration.md +166 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +336 -0
- package/skills/wix-manage/references/rich-content/ricos-converter-service.md +295 -0
- package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +59 -0
- package/skills/wix-manage/references/sites/create-site-from-template.md +183 -0
- package/skills/wix-manage/references/sites/query-sites.md +125 -0
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +57 -0
- package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +1041 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v1.md +125 -0
- package/skills/wix-manage/references/stores/create-product-from-image.md +824 -0
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +566 -0
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +194 -0
- package/skills/wix-manage/references/stores/query-products-catalog-v1.md +80 -0
- package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +99 -0
- package/skills/wix-manage/references/stores/update-product-pre-order.md +213 -0
- package/skills/wix-manage/references/stores/update-product-with-options.md +318 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# Recipe: Portfolio / Projects Showcase
|
|
2
|
+
|
|
3
|
+
Build a visual portfolio using `@wix/data` — project grid with category filtering, image galleries, and project detail pages. Use for work samples, case studies, creative projects, or any visual showcase.
|
|
4
|
+
|
|
5
|
+
> Read `CMS_FOUNDATIONS.md` first for shared patterns (service module, image resolution, elevation, MCP seeding).
|
|
6
|
+
|
|
7
|
+
## Collection Schema
|
|
8
|
+
|
|
9
|
+
Create a collection in the Wix dashboard → CMS with these fields:
|
|
10
|
+
|
|
11
|
+
| Field | Type | Purpose |
|
|
12
|
+
|-------|------|---------|
|
|
13
|
+
| `title` | Text | Project name |
|
|
14
|
+
| `slug` | Text | URL-friendly identifier |
|
|
15
|
+
| `category` | Text | Project category (e.g., "Branding", "Web Design") |
|
|
16
|
+
| `description` | Rich Text | Project writeup (HTML) |
|
|
17
|
+
| `client` | Text | Client or company name |
|
|
18
|
+
| `year` | Text | Year completed |
|
|
19
|
+
| `coverImage` | Image | Main project image (`wix:image://` — resolve with `media.getScaledToFillImageUrl()`) |
|
|
20
|
+
| `galleryImages` | Image Gallery | Additional project images (multi-image array) |
|
|
21
|
+
| `tags` | Text | Comma-separated tags |
|
|
22
|
+
| `featured` | Boolean | Show on home page |
|
|
23
|
+
| `orderIndex` | Number | Manual sort order |
|
|
24
|
+
|
|
25
|
+
## Files to Create
|
|
26
|
+
|
|
27
|
+
| File | Purpose |
|
|
28
|
+
|------|---------|
|
|
29
|
+
| `src/lib/portfolio.ts` | Service module — queries, category filtering, featured projects |
|
|
30
|
+
| `src/pages/work/index.astro` | Project grid with category filter tabs |
|
|
31
|
+
| `src/pages/work/[slug].astro` | Project detail with image gallery |
|
|
32
|
+
| `src/components/ProjectCard.astro` | Visual card for grid layout |
|
|
33
|
+
| `src/components/CategoryFilter.astro` | URL-param-based pill buttons |
|
|
34
|
+
| `src/components/ImageGallery.tsx` | React island for lightbox/carousel |
|
|
35
|
+
|
|
36
|
+
## Implementation
|
|
37
|
+
|
|
38
|
+
### 1. Portfolio Service Module (`src/lib/portfolio.ts`)
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import * as items from "@wix/wix-data-items-sdk";
|
|
42
|
+
import { auth } from "@wix/essentials";
|
|
43
|
+
import { media } from "@wix/sdk";
|
|
44
|
+
|
|
45
|
+
const COLLECTION_ID = "Projects";
|
|
46
|
+
|
|
47
|
+
export interface Project {
|
|
48
|
+
_id: string;
|
|
49
|
+
title: string;
|
|
50
|
+
slug: string;
|
|
51
|
+
category: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
client?: string;
|
|
54
|
+
year?: string;
|
|
55
|
+
coverImage?: string;
|
|
56
|
+
galleryImages: string[];
|
|
57
|
+
tags: string[];
|
|
58
|
+
featured: boolean;
|
|
59
|
+
orderIndex: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveImage(url: string | undefined, width = 800, height = 600): string | undefined {
|
|
63
|
+
if (!url) return undefined;
|
|
64
|
+
return media.getScaledToFillImageUrl(url, width, height, {});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveImages(urls: string[] | undefined, width = 1200, height = 800): string[] {
|
|
68
|
+
if (!urls || urls.length === 0) return [];
|
|
69
|
+
return urls.map((url) => media.getScaledToFillImageUrl(url, width, height, {}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function mapProject(item: Record<string, any>): Project {
|
|
73
|
+
return {
|
|
74
|
+
_id: item._id,
|
|
75
|
+
title: item.title ?? "",
|
|
76
|
+
slug: item.slug ?? "",
|
|
77
|
+
category: item.category ?? "",
|
|
78
|
+
description: item.description,
|
|
79
|
+
client: item.client,
|
|
80
|
+
year: item.year,
|
|
81
|
+
coverImage: resolveImage(item.coverImage),
|
|
82
|
+
galleryImages: resolveImages(item.galleryImages),
|
|
83
|
+
tags: item.tags ? item.tags.split(",").map((t: string) => t.trim()) : [],
|
|
84
|
+
featured: item.featured ?? false,
|
|
85
|
+
orderIndex: item.orderIndex ?? 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function queryProjects(): Promise<Project[]> {
|
|
90
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
91
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
92
|
+
.ascending("orderIndex")
|
|
93
|
+
.limit(50)
|
|
94
|
+
.find();
|
|
95
|
+
|
|
96
|
+
return results.map(mapProject);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function getProjectBySlug(slug: string): Promise<Project | null> {
|
|
100
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
101
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
102
|
+
.eq("slug", slug)
|
|
103
|
+
.limit(1)
|
|
104
|
+
.find();
|
|
105
|
+
|
|
106
|
+
const item = results[0];
|
|
107
|
+
if (!item) return null;
|
|
108
|
+
return mapProject(item);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function queryFeaturedProjects(limit = 3): Promise<Project[]> {
|
|
112
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
113
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
114
|
+
.eq("featured", true)
|
|
115
|
+
.ascending("orderIndex")
|
|
116
|
+
.limit(limit)
|
|
117
|
+
.find();
|
|
118
|
+
|
|
119
|
+
return results.map(mapProject);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Key details:
|
|
124
|
+
- `resolveImages()` handles the multi-image gallery field — iterates and resolves each URL
|
|
125
|
+
- `tags` stored as comma-separated text, split into array at query time
|
|
126
|
+
- `queryFeaturedProjects()` is for home page sections — query `featured === true`
|
|
127
|
+
- Update `COLLECTION_ID` to match the exact collection name from the Wix dashboard (no namespace prefix for native collections)
|
|
128
|
+
|
|
129
|
+
### 2. Category Filter (`src/components/CategoryFilter.astro`)
|
|
130
|
+
|
|
131
|
+
```astro
|
|
132
|
+
---
|
|
133
|
+
interface Props {
|
|
134
|
+
categories: string[];
|
|
135
|
+
activeCategory: string | null;
|
|
136
|
+
basePath: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { categories, activeCategory, basePath } = Astro.props;
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
<nav>
|
|
143
|
+
<a href={basePath} class:list={[!activeCategory && "active"]}>All</a>
|
|
144
|
+
{categories.map((cat) => (
|
|
145
|
+
<a
|
|
146
|
+
href={`${basePath}?category=${encodeURIComponent(cat)}`}
|
|
147
|
+
class:list={[activeCategory === cat && "active"]}
|
|
148
|
+
>{cat}</a>
|
|
149
|
+
))}
|
|
150
|
+
</nav>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> **Styling note:** CategoryFilter styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Category Filter.
|
|
154
|
+
|
|
155
|
+
> Server-rendered, no JavaScript. Category is read from `Astro.url.searchParams` in the page.
|
|
156
|
+
|
|
157
|
+
### 3. Project Card (`src/components/ProjectCard.astro`)
|
|
158
|
+
|
|
159
|
+
```astro
|
|
160
|
+
---
|
|
161
|
+
interface Props {
|
|
162
|
+
project: {
|
|
163
|
+
title: string;
|
|
164
|
+
slug: string;
|
|
165
|
+
category: string;
|
|
166
|
+
coverImage?: string;
|
|
167
|
+
client?: string;
|
|
168
|
+
year?: string;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const { project } = Astro.props;
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
<a href={`/work/${project.slug}`}>
|
|
176
|
+
<div>
|
|
177
|
+
{project.coverImage ? (
|
|
178
|
+
<img src={project.coverImage} alt={project.title} />
|
|
179
|
+
) : (
|
|
180
|
+
<div>No image</div>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
<div>
|
|
184
|
+
<span>{project.category}</span>
|
|
185
|
+
{project.year && <span>· {project.year}</span>}
|
|
186
|
+
</div>
|
|
187
|
+
<h3>{project.title}</h3>
|
|
188
|
+
{project.client && <p>{project.client}</p>}
|
|
189
|
+
</a>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> **Styling note:** ProjectCard styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Project Card.
|
|
193
|
+
|
|
194
|
+
### 4. Project Grid Page (`src/pages/work/index.astro`)
|
|
195
|
+
|
|
196
|
+
```astro
|
|
197
|
+
---
|
|
198
|
+
import Layout from "../../layouts/Layout.astro";
|
|
199
|
+
import ProjectCard from "../../components/ProjectCard.astro";
|
|
200
|
+
import CategoryFilter from "../../components/CategoryFilter.astro";
|
|
201
|
+
import { queryProjects } from "../../lib/portfolio";
|
|
202
|
+
|
|
203
|
+
const activeCategory = Astro.url.searchParams.get("category");
|
|
204
|
+
const allProjects = await queryProjects();
|
|
205
|
+
const filtered = activeCategory
|
|
206
|
+
? allProjects.filter((p) => p.category === activeCategory)
|
|
207
|
+
: allProjects;
|
|
208
|
+
const categories = [...new Set(allProjects.map((p) => p.category).filter(Boolean))];
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
<Layout title="Work">
|
|
212
|
+
<main>
|
|
213
|
+
<div>
|
|
214
|
+
<h1>Work</h1>
|
|
215
|
+
|
|
216
|
+
<CategoryFilter categories={categories} activeCategory={activeCategory} basePath="/work" />
|
|
217
|
+
|
|
218
|
+
<div>
|
|
219
|
+
{filtered.map((project) => (
|
|
220
|
+
<ProjectCard project={project} />
|
|
221
|
+
))}
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{filtered.length === 0 && (
|
|
225
|
+
<p>No projects found.</p>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
</main>
|
|
229
|
+
</Layout>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 5. Project Detail Page (`src/pages/work/[slug].astro`)
|
|
233
|
+
|
|
234
|
+
```astro
|
|
235
|
+
---
|
|
236
|
+
import Layout from "../../layouts/Layout.astro";
|
|
237
|
+
import ImageGallery from "../../components/ImageGallery";
|
|
238
|
+
import { getProjectBySlug } from "../../lib/portfolio";
|
|
239
|
+
|
|
240
|
+
const { slug } = Astro.params;
|
|
241
|
+
const project = await getProjectBySlug(slug!);
|
|
242
|
+
if (!project) return Astro.redirect("/404");
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
<Layout title={project.title}>
|
|
246
|
+
<main>
|
|
247
|
+
<div>
|
|
248
|
+
<a href="/work">
|
|
249
|
+
← Back to work
|
|
250
|
+
</a>
|
|
251
|
+
|
|
252
|
+
{project.coverImage && (
|
|
253
|
+
<div>
|
|
254
|
+
<img
|
|
255
|
+
src={project.coverImage}
|
|
256
|
+
alt={project.title}
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
259
|
+
)}
|
|
260
|
+
|
|
261
|
+
<div>
|
|
262
|
+
<span>{project.category}</span>
|
|
263
|
+
{project.client && (
|
|
264
|
+
<>
|
|
265
|
+
<span>·</span>
|
|
266
|
+
<span>{project.client}</span>
|
|
267
|
+
</>
|
|
268
|
+
)}
|
|
269
|
+
{project.year && (
|
|
270
|
+
<>
|
|
271
|
+
<span>·</span>
|
|
272
|
+
<span>{project.year}</span>
|
|
273
|
+
</>
|
|
274
|
+
)}
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<h1>{project.title}</h1>
|
|
278
|
+
|
|
279
|
+
{project.description && (
|
|
280
|
+
<article set:html={project.description} />
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
{project.galleryImages.length > 0 && (
|
|
284
|
+
<ImageGallery client:only="react" images={project.galleryImages} alt={project.title} />
|
|
285
|
+
)}
|
|
286
|
+
|
|
287
|
+
{project.tags.length > 0 && (
|
|
288
|
+
<div>
|
|
289
|
+
{project.tags.map((tag) => (
|
|
290
|
+
<span>{tag}</span>
|
|
291
|
+
))}
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
294
|
+
</div>
|
|
295
|
+
</main>
|
|
296
|
+
</Layout>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### 6. Image Gallery React Island (`src/components/ImageGallery.tsx`)
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
import { useState } from "react";
|
|
303
|
+
|
|
304
|
+
interface Props {
|
|
305
|
+
images: string[];
|
|
306
|
+
alt: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export default function ImageGallery({ images, alt }: Props) {
|
|
310
|
+
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
|
311
|
+
|
|
312
|
+
if (images.length === 0) return null;
|
|
313
|
+
|
|
314
|
+
return (
|
|
315
|
+
<>
|
|
316
|
+
<div className="gallery-grid">
|
|
317
|
+
{images.map((src, i) => (
|
|
318
|
+
<button
|
|
319
|
+
key={i}
|
|
320
|
+
onClick={() => setActiveIndex(i)}
|
|
321
|
+
className="gallery-item"
|
|
322
|
+
>
|
|
323
|
+
<img
|
|
324
|
+
src={src}
|
|
325
|
+
alt={`${alt} — image ${i + 1}`}
|
|
326
|
+
/>
|
|
327
|
+
</button>
|
|
328
|
+
))}
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
{activeIndex !== null && (
|
|
332
|
+
<div
|
|
333
|
+
className="lightbox-overlay"
|
|
334
|
+
onClick={() => setActiveIndex(null)}
|
|
335
|
+
>
|
|
336
|
+
<button
|
|
337
|
+
className="lightbox-close"
|
|
338
|
+
onClick={() => setActiveIndex(null)}
|
|
339
|
+
>
|
|
340
|
+
×
|
|
341
|
+
</button>
|
|
342
|
+
|
|
343
|
+
{images.length > 1 && (
|
|
344
|
+
<>
|
|
345
|
+
<button
|
|
346
|
+
className="lightbox-nav prev"
|
|
347
|
+
onClick={(e) => {
|
|
348
|
+
e.stopPropagation();
|
|
349
|
+
setActiveIndex((activeIndex - 1 + images.length) % images.length);
|
|
350
|
+
}}
|
|
351
|
+
>
|
|
352
|
+
‹
|
|
353
|
+
</button>
|
|
354
|
+
<button
|
|
355
|
+
className="lightbox-nav next"
|
|
356
|
+
onClick={(e) => {
|
|
357
|
+
e.stopPropagation();
|
|
358
|
+
setActiveIndex((activeIndex + 1) % images.length);
|
|
359
|
+
}}
|
|
360
|
+
>
|
|
361
|
+
›
|
|
362
|
+
</button>
|
|
363
|
+
</>
|
|
364
|
+
)}
|
|
365
|
+
|
|
366
|
+
<img
|
|
367
|
+
src={images[activeIndex]}
|
|
368
|
+
alt={`${alt} — image ${activeIndex + 1}`}
|
|
369
|
+
className="lightbox-image"
|
|
370
|
+
onClick={(e) => e.stopPropagation()}
|
|
371
|
+
/>
|
|
372
|
+
</div>
|
|
373
|
+
)}
|
|
374
|
+
</>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
> **Styling note:** Uses `.gallery-grid`, `.gallery-item`, `.lightbox-overlay`, `.lightbox-image`, `.lightbox-nav`, `.lightbox-close` from the designed component's `<style is:global>` block. See `COMPONENT_PATTERNS.md` → Image Gallery / Lightbox.
|
|
380
|
+
|
|
381
|
+
Key details:
|
|
382
|
+
- Renders as a grid; clicking opens a lightbox overlay
|
|
383
|
+
- Previous/next navigation wraps around
|
|
384
|
+
- `client:only="react"` — requires client-side rendering for `useState`
|
|
385
|
+
- No external dependencies — pure React
|
|
386
|
+
|
|
387
|
+
## Home Page Integration
|
|
388
|
+
|
|
389
|
+
To show featured projects on the home page, import `queryFeaturedProjects`:
|
|
390
|
+
|
|
391
|
+
```astro
|
|
392
|
+
---
|
|
393
|
+
import ProjectCard from "../components/ProjectCard.astro";
|
|
394
|
+
import { queryFeaturedProjects } from "../lib/portfolio";
|
|
395
|
+
|
|
396
|
+
const featured = await queryFeaturedProjects(3);
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
<section>
|
|
400
|
+
<div>
|
|
401
|
+
<h2>Featured Work</h2>
|
|
402
|
+
<div>
|
|
403
|
+
{featured.map((project) => (
|
|
404
|
+
<ProjectCard project={project} />
|
|
405
|
+
))}
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
</section>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Seed with Images
|
|
412
|
+
|
|
413
|
+
After seeding projects via MCP, generate cover images (and optionally gallery images) following `../../shared/IMAGE_GENERATION.md` and `CMS_FOUNDATIONS.md` → "MCP Seeding with Images".
|
|
414
|
+
|
|
415
|
+
**Cover image prompt template:**
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
Project showcase image for [TITLE]. [BRAND AESTHETIC]. Color palette: [BRAND COLORS]. Editorial style. No text, no watermarks
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Patch field:** `coverImage`
|
|
422
|
+
|
|
423
|
+
**Optional gallery images** — generate 2-3 additional images per project for the `galleryImages` array. Note: this is slow (each image takes ~10-15s), so only do this if the user wants a fully populated portfolio.
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
Gallery image [N] for [TITLE]: [describe a specific aspect/angle of the project]. [BRAND AESTHETIC]. [BRAND COLORS]. No text, no watermarks
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Patch fields:** `coverImage`, optionally `galleryImages` (as an array of wixstatic URLs)
|
|
430
|
+
|
|
431
|
+
Example patch:
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
CallWixSiteAPI: PATCH /wix-data/v2/items/{projectId}
|
|
435
|
+
body: {
|
|
436
|
+
"dataCollectionId": "Projects",
|
|
437
|
+
"dataItem": {
|
|
438
|
+
"data": {
|
|
439
|
+
"coverImage": "<wixstatic-url>",
|
|
440
|
+
"galleryImages": ["<wixstatic-url-1>", "<wixstatic-url-2>"]
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Testing
|
|
447
|
+
|
|
448
|
+
1. Create a "Projects" collection in the Wix dashboard → CMS with the schema above
|
|
449
|
+
2. Add 3+ projects with cover images, categories, and at least one with gallery images
|
|
450
|
+
3. Mark 1–2 as `featured: true`
|
|
451
|
+
4. Run `npx @wix/cli dev`
|
|
452
|
+
5. `/work` — grid with category filter tabs
|
|
453
|
+
6. Click a category — filters projects
|
|
454
|
+
7. Click a project — detail page with gallery lightbox
|