@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,356 @@
|
|
|
1
|
+
# Recipe: Resource Library / Downloads
|
|
2
|
+
|
|
3
|
+
Build a downloadable resource library using `@wix/data` — file listings with category filtering, file type badges, and detail pages with download buttons. Use for documents, guides, templates, whitepapers, or any downloadable assets.
|
|
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 | Resource name |
|
|
14
|
+
| `slug` | Text | URL-friendly identifier |
|
|
15
|
+
| `description` | Rich Text | Resource description (HTML) |
|
|
16
|
+
| `category` | Text | Resource category (e.g., "Guides", "Templates") |
|
|
17
|
+
| `fileType` | Text | File extension — `PDF`, `DOC`, `ZIP`, `XLS`, etc. |
|
|
18
|
+
| `fileUrl` | URL | Download URL (external link or Wix media URL) |
|
|
19
|
+
| `fileSize` | Text | Human-readable file size (e.g., "2.4 MB") |
|
|
20
|
+
| `coverImage` | Image | Thumbnail (`wix:image://` — resolve with `media.getScaledToFillImageUrl()`) |
|
|
21
|
+
| `published` | Boolean | Publish status |
|
|
22
|
+
| `_createdDate` | Date | Auto-populated creation date |
|
|
23
|
+
|
|
24
|
+
## Files to Create
|
|
25
|
+
|
|
26
|
+
| File | Purpose |
|
|
27
|
+
|------|---------|
|
|
28
|
+
| `src/lib/resources.ts` | Service module — queries, category filtering, related resources |
|
|
29
|
+
| `src/pages/resources/index.astro` | Listing with category filter + file type badges |
|
|
30
|
+
| `src/pages/resources/[slug].astro` | Detail page with download button + related resources |
|
|
31
|
+
| `src/components/ResourceCard.astro` | Card with file type icon, title, size |
|
|
32
|
+
| `src/components/FileTypeIcon.astro` | SVG switch for PDF/DOC/ZIP/generic |
|
|
33
|
+
|
|
34
|
+
## Implementation
|
|
35
|
+
|
|
36
|
+
### 1. Resources Service Module (`src/lib/resources.ts`)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import * as items from "@wix/wix-data-items-sdk";
|
|
40
|
+
import { auth } from "@wix/essentials";
|
|
41
|
+
import { media } from "@wix/sdk";
|
|
42
|
+
|
|
43
|
+
const COLLECTION_ID = "Resources";
|
|
44
|
+
|
|
45
|
+
export interface Resource {
|
|
46
|
+
_id: string;
|
|
47
|
+
title: string;
|
|
48
|
+
slug: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
category: string;
|
|
51
|
+
fileType: string;
|
|
52
|
+
fileUrl: string;
|
|
53
|
+
fileSize?: string;
|
|
54
|
+
coverImage?: string;
|
|
55
|
+
published: boolean;
|
|
56
|
+
_createdDate?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveImage(url: string | undefined): string | undefined {
|
|
60
|
+
if (!url) return undefined;
|
|
61
|
+
return media.getScaledToFillImageUrl(url, 600, 400, {});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function mapResource(item: Record<string, any>): Resource {
|
|
65
|
+
return {
|
|
66
|
+
_id: item._id,
|
|
67
|
+
title: item.title ?? "",
|
|
68
|
+
slug: item.slug ?? "",
|
|
69
|
+
description: item.description,
|
|
70
|
+
category: item.category ?? "",
|
|
71
|
+
fileType: (item.fileType ?? "FILE").toUpperCase(),
|
|
72
|
+
fileUrl: item.fileUrl ?? "",
|
|
73
|
+
fileSize: item.fileSize,
|
|
74
|
+
coverImage: resolveImage(item.coverImage),
|
|
75
|
+
published: item.published ?? false,
|
|
76
|
+
_createdDate: item._createdDate,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function queryResources(): Promise<Resource[]> {
|
|
81
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
82
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
83
|
+
.eq("published", true)
|
|
84
|
+
.descending("_createdDate")
|
|
85
|
+
.limit(50)
|
|
86
|
+
.find();
|
|
87
|
+
|
|
88
|
+
return results.map(mapResource);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function getResourceBySlug(slug: string): Promise<Resource | null> {
|
|
92
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
93
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
94
|
+
.eq("slug", slug)
|
|
95
|
+
.eq("published", true)
|
|
96
|
+
.limit(1)
|
|
97
|
+
.find();
|
|
98
|
+
|
|
99
|
+
const item = results[0];
|
|
100
|
+
if (!item) return null;
|
|
101
|
+
return mapResource(item);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function queryRelatedResources(category: string, excludeId: string, limit = 3): Promise<Resource[]> {
|
|
105
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
106
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
107
|
+
.eq("published", true)
|
|
108
|
+
.eq("category", category)
|
|
109
|
+
.ne("_id", excludeId)
|
|
110
|
+
.descending("_createdDate")
|
|
111
|
+
.limit(limit)
|
|
112
|
+
.find();
|
|
113
|
+
|
|
114
|
+
return results.map(mapResource);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Key details:
|
|
119
|
+
- `queryRelatedResources()` finds same-category items excluding the current one
|
|
120
|
+
- `fileType` normalized to uppercase for consistent badge display
|
|
121
|
+
- Sorted by `_createdDate` descending (newest first)
|
|
122
|
+
- Update `COLLECTION_ID` to match the exact collection name from the Wix dashboard (no namespace prefix for native collections)
|
|
123
|
+
|
|
124
|
+
### 2. File Type Icon (`src/components/FileTypeIcon.astro`)
|
|
125
|
+
|
|
126
|
+
```astro
|
|
127
|
+
---
|
|
128
|
+
interface Props {
|
|
129
|
+
type: string;
|
|
130
|
+
class?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const { type, class: className = "w-8 h-8" } = Astro.props;
|
|
134
|
+
const fileType = type.toUpperCase();
|
|
135
|
+
|
|
136
|
+
// File type colors are applied by the design skill's component styles
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
<div class={className}>
|
|
140
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="w-full h-full">
|
|
141
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
|
|
142
|
+
<polyline points="14 2 14 8 20 8" />
|
|
143
|
+
<text x="12" y="17" text-anchor="middle" font-size="5" fill="currentColor" stroke="none" font-weight="bold">{fileType.slice(0, 4)}</text>
|
|
144
|
+
</svg>
|
|
145
|
+
</div>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Key details:
|
|
149
|
+
- Color-coded by file type — red for PDF, blue for DOC, green for XLS, yellow for ZIP
|
|
150
|
+
- Falls back to neutral gray for unknown types
|
|
151
|
+
- File type label rendered inside the document icon SVG
|
|
152
|
+
|
|
153
|
+
### 3. Resource Card (`src/components/ResourceCard.astro`)
|
|
154
|
+
|
|
155
|
+
```astro
|
|
156
|
+
---
|
|
157
|
+
import FileTypeIcon from "./FileTypeIcon.astro";
|
|
158
|
+
|
|
159
|
+
interface Props {
|
|
160
|
+
resource: {
|
|
161
|
+
title: string;
|
|
162
|
+
slug: string;
|
|
163
|
+
category: string;
|
|
164
|
+
fileType: string;
|
|
165
|
+
fileSize?: string;
|
|
166
|
+
coverImage?: string;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const { resource } = Astro.props;
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
<a href={`/resources/${resource.slug}`}>
|
|
174
|
+
{resource.coverImage ? (
|
|
175
|
+
<div><img src={resource.coverImage} alt={resource.title} /></div>
|
|
176
|
+
) : (
|
|
177
|
+
<div><FileTypeIcon type={resource.fileType} /></div>
|
|
178
|
+
)}
|
|
179
|
+
<div>
|
|
180
|
+
<div>
|
|
181
|
+
<span>{resource.fileType}</span>
|
|
182
|
+
{resource.fileSize && <span>{resource.fileSize}</span>}
|
|
183
|
+
</div>
|
|
184
|
+
<h3>{resource.title}</h3>
|
|
185
|
+
<p>{resource.category}</p>
|
|
186
|
+
</div>
|
|
187
|
+
</a>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> **Styling note:** ResourceCard styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Resource Card.
|
|
191
|
+
|
|
192
|
+
### 4. Resource Listing Page (`src/pages/resources/index.astro`)
|
|
193
|
+
|
|
194
|
+
```astro
|
|
195
|
+
---
|
|
196
|
+
import Layout from "../../layouts/Layout.astro";
|
|
197
|
+
import ResourceCard from "../../components/ResourceCard.astro";
|
|
198
|
+
import CategoryFilter from "../../components/CategoryFilter.astro";
|
|
199
|
+
import { queryResources } from "../../lib/resources";
|
|
200
|
+
|
|
201
|
+
const activeCategory = Astro.url.searchParams.get("category");
|
|
202
|
+
const allResources = await queryResources();
|
|
203
|
+
const filtered = activeCategory
|
|
204
|
+
? allResources.filter((r) => r.category === activeCategory)
|
|
205
|
+
: allResources;
|
|
206
|
+
const categories = [...new Set(allResources.map((r) => r.category).filter(Boolean))];
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
<Layout title="Resources">
|
|
210
|
+
<main>
|
|
211
|
+
<div>
|
|
212
|
+
<h1>Resources</h1>
|
|
213
|
+
<p>Download guides, templates, and documents.</p>
|
|
214
|
+
|
|
215
|
+
<CategoryFilter categories={categories} activeCategory={activeCategory} basePath="/resources" />
|
|
216
|
+
|
|
217
|
+
<div>
|
|
218
|
+
{filtered.map((resource) => (
|
|
219
|
+
<ResourceCard resource={resource} />
|
|
220
|
+
))}
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
{filtered.length === 0 && (
|
|
224
|
+
<p>No resources found.</p>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
</main>
|
|
228
|
+
</Layout>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> Reuses the same `CategoryFilter.astro` component from the portfolio use case. If building both, no need to create it twice.
|
|
232
|
+
|
|
233
|
+
### 5. Resource Detail Page (`src/pages/resources/[slug].astro`)
|
|
234
|
+
|
|
235
|
+
```astro
|
|
236
|
+
---
|
|
237
|
+
import Layout from "../../layouts/Layout.astro";
|
|
238
|
+
import ResourceCard from "../../components/ResourceCard.astro";
|
|
239
|
+
import FileTypeIcon from "../../components/FileTypeIcon.astro";
|
|
240
|
+
import { getResourceBySlug, queryRelatedResources } from "../../lib/resources";
|
|
241
|
+
|
|
242
|
+
const { slug } = Astro.params;
|
|
243
|
+
const resource = await getResourceBySlug(slug!);
|
|
244
|
+
if (!resource) return Astro.redirect("/404");
|
|
245
|
+
|
|
246
|
+
const related = await queryRelatedResources(resource.category, resource._id);
|
|
247
|
+
|
|
248
|
+
const date = resource._createdDate
|
|
249
|
+
? new Date(resource._createdDate).toLocaleDateString("en-US", {
|
|
250
|
+
year: "numeric",
|
|
251
|
+
month: "long",
|
|
252
|
+
day: "numeric",
|
|
253
|
+
})
|
|
254
|
+
: "";
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
<Layout title={resource.title}>
|
|
258
|
+
<main>
|
|
259
|
+
<div>
|
|
260
|
+
<a href="/resources">
|
|
261
|
+
← Back to resources
|
|
262
|
+
</a>
|
|
263
|
+
|
|
264
|
+
<div>
|
|
265
|
+
<FileTypeIcon type={resource.fileType} />
|
|
266
|
+
<div>
|
|
267
|
+
<div>
|
|
268
|
+
<span>{resource.fileType}</span>
|
|
269
|
+
{resource.fileSize && (
|
|
270
|
+
<span>{resource.fileSize}</span>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
{date && <p>Added {date}</p>}
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<h1>{resource.title}</h1>
|
|
278
|
+
|
|
279
|
+
{resource.coverImage && (
|
|
280
|
+
<div>
|
|
281
|
+
<img
|
|
282
|
+
src={resource.coverImage}
|
|
283
|
+
alt={resource.title}
|
|
284
|
+
/>
|
|
285
|
+
</div>
|
|
286
|
+
)}
|
|
287
|
+
|
|
288
|
+
{resource.description && (
|
|
289
|
+
<article set:html={resource.description} />
|
|
290
|
+
)}
|
|
291
|
+
|
|
292
|
+
{resource.fileUrl && (
|
|
293
|
+
<a
|
|
294
|
+
href={resource.fileUrl}
|
|
295
|
+
download
|
|
296
|
+
>
|
|
297
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
|
298
|
+
Download {resource.fileType}
|
|
299
|
+
</a>
|
|
300
|
+
)}
|
|
301
|
+
|
|
302
|
+
{related.length > 0 && (
|
|
303
|
+
<section>
|
|
304
|
+
<h2>Related Resources</h2>
|
|
305
|
+
<div>
|
|
306
|
+
{related.map((r) => (
|
|
307
|
+
<ResourceCard resource={r} />
|
|
308
|
+
))}
|
|
309
|
+
</div>
|
|
310
|
+
</section>
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
</main>
|
|
314
|
+
</Layout>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Key details:
|
|
318
|
+
- `<a download>` triggers browser download for the file
|
|
319
|
+
- Related resources query pulls same-category items, excluding the current one
|
|
320
|
+
- File type icon and badge prominently displayed
|
|
321
|
+
|
|
322
|
+
## Seed with Images
|
|
323
|
+
|
|
324
|
+
After seeding resources via MCP, generate cover images following `../../shared/IMAGE_GENERATION.md` and `CMS_FOUNDATIONS.md` → "MCP Seeding with Images".
|
|
325
|
+
|
|
326
|
+
**Prompt template:**
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
Cover image for [TITLE], a [FILE TYPE]. Abstract [BRAND AESTHETIC]. Color tones: [BRAND COLORS]. No text, no watermarks
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Patch field:** `coverImage`
|
|
333
|
+
|
|
334
|
+
Example patch:
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
CallWixSiteAPI: PATCH /wix-data/v2/items/{resourceId}
|
|
338
|
+
body: {
|
|
339
|
+
"dataCollectionId": "Resources",
|
|
340
|
+
"dataItem": {
|
|
341
|
+
"data": {
|
|
342
|
+
"coverImage": "<wixstatic-url>"
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Testing
|
|
349
|
+
|
|
350
|
+
1. Create a "Resources" collection in the Wix dashboard → CMS with the schema above
|
|
351
|
+
2. Add 4+ resources across 2+ categories with different file types (PDF, DOC, ZIP)
|
|
352
|
+
3. Include `fileUrl` links (can be any downloadable URL for testing)
|
|
353
|
+
4. Run `npx @wix/cli dev`
|
|
354
|
+
5. `/resources` — shows all resources with category filters and file type badges
|
|
355
|
+
6. Click a category — filters resources
|
|
356
|
+
7. Click a resource — detail page with download button and related resources
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Recipe: Team / Staff Directory
|
|
2
|
+
|
|
3
|
+
Build a team directory using `@wix/data` — staff cards grouped by department with individual profile pages. Use for about us, our team, meet the staff, or any people directory.
|
|
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
|
+
| `name` | Text | Full name |
|
|
14
|
+
| `slug` | Text | URL-friendly identifier |
|
|
15
|
+
| `role` | Text | Job title or role |
|
|
16
|
+
| `department` | Text | Department or team (e.g., "Engineering", "Design") |
|
|
17
|
+
| `bio` | Rich Text | Bio or description (HTML) |
|
|
18
|
+
| `photo` | Image | Headshot (`wix:image://` — resolve with `media.getScaledToFillImageUrl()`) |
|
|
19
|
+
| `email` | Text | Contact email |
|
|
20
|
+
| `linkedIn` | URL | LinkedIn profile URL |
|
|
21
|
+
| `twitter` | URL | Twitter/X profile URL |
|
|
22
|
+
| `orderIndex` | Number | Manual sort order within department |
|
|
23
|
+
|
|
24
|
+
## Files to Create
|
|
25
|
+
|
|
26
|
+
| File | Purpose |
|
|
27
|
+
|------|---------|
|
|
28
|
+
| `src/lib/team.ts` | Service module — queries, department grouping |
|
|
29
|
+
| `src/pages/team/index.astro` | Department-sectioned directory |
|
|
30
|
+
| `src/pages/team/[slug].astro` | Individual member profile |
|
|
31
|
+
| `src/components/TeamMemberCard.astro` | Photo, name, role, social icons |
|
|
32
|
+
|
|
33
|
+
## Implementation
|
|
34
|
+
|
|
35
|
+
### 1. Team Service Module (`src/lib/team.ts`)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import * as items from "@wix/wix-data-items-sdk";
|
|
39
|
+
import { auth } from "@wix/essentials";
|
|
40
|
+
import { media } from "@wix/sdk";
|
|
41
|
+
|
|
42
|
+
const COLLECTION_ID = "Team";
|
|
43
|
+
|
|
44
|
+
export interface TeamMember {
|
|
45
|
+
_id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
slug: string;
|
|
48
|
+
role: string;
|
|
49
|
+
department: string;
|
|
50
|
+
bio?: string;
|
|
51
|
+
photo?: string;
|
|
52
|
+
email?: string;
|
|
53
|
+
linkedIn?: string;
|
|
54
|
+
twitter?: string;
|
|
55
|
+
orderIndex: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolvePhoto(url: string | undefined): string | undefined {
|
|
59
|
+
if (!url) return undefined;
|
|
60
|
+
return media.getScaledToFillImageUrl(url, 400, 400, {});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function mapMember(item: Record<string, any>): TeamMember {
|
|
64
|
+
return {
|
|
65
|
+
_id: item._id,
|
|
66
|
+
name: item.name ?? "",
|
|
67
|
+
slug: item.slug ?? "",
|
|
68
|
+
role: item.role ?? "",
|
|
69
|
+
department: item.department ?? "",
|
|
70
|
+
bio: item.bio,
|
|
71
|
+
photo: resolvePhoto(item.photo),
|
|
72
|
+
email: item.email,
|
|
73
|
+
linkedIn: item.linkedIn,
|
|
74
|
+
twitter: item.twitter,
|
|
75
|
+
orderIndex: item.orderIndex ?? 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function queryTeamMembers(): Promise<TeamMember[]> {
|
|
80
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
81
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
82
|
+
.ascending("orderIndex")
|
|
83
|
+
.limit(100)
|
|
84
|
+
.find();
|
|
85
|
+
|
|
86
|
+
return results.map(mapMember);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function getMemberBySlug(slug: string): Promise<TeamMember | null> {
|
|
90
|
+
const elevatedQuery = auth.elevate(items.query);
|
|
91
|
+
const { items: results } = await elevatedQuery(COLLECTION_ID)
|
|
92
|
+
.eq("slug", slug)
|
|
93
|
+
.limit(1)
|
|
94
|
+
.find();
|
|
95
|
+
|
|
96
|
+
const item = results[0];
|
|
97
|
+
if (!item) return null;
|
|
98
|
+
return mapMember(item);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function groupByDepartment(members: TeamMember[]): Map<string, TeamMember[]> {
|
|
102
|
+
return members.reduce((map, member) => {
|
|
103
|
+
const dept = member.department || "Team";
|
|
104
|
+
if (!map.has(dept)) map.set(dept, []);
|
|
105
|
+
map.get(dept)!.push(member);
|
|
106
|
+
return map;
|
|
107
|
+
}, new Map<string, TeamMember[]>());
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Key details:
|
|
112
|
+
- `resolvePhoto()` uses 400x400 for square headshot crops
|
|
113
|
+
- `groupByDepartment()` returns a `Map` preserving insertion order — departments appear in the order of their first member
|
|
114
|
+
- Update `COLLECTION_ID` to match the exact collection name from the Wix dashboard (no namespace prefix for native collections)
|
|
115
|
+
|
|
116
|
+
### 2. Team Member Card (`src/components/TeamMemberCard.astro`)
|
|
117
|
+
|
|
118
|
+
```astro
|
|
119
|
+
---
|
|
120
|
+
interface Props {
|
|
121
|
+
member: {
|
|
122
|
+
name: string;
|
|
123
|
+
slug: string;
|
|
124
|
+
role: string;
|
|
125
|
+
photo?: string;
|
|
126
|
+
email?: string;
|
|
127
|
+
linkedIn?: string;
|
|
128
|
+
twitter?: string;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const { member } = Astro.props;
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
<a href={`/team/${member.slug}`}>
|
|
136
|
+
<div>
|
|
137
|
+
{member.photo ? (
|
|
138
|
+
<img src={member.photo} alt={member.name} />
|
|
139
|
+
) : (
|
|
140
|
+
<div>{member.name.charAt(0)}</div>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
<h3>{member.name}</h3>
|
|
144
|
+
<p>{member.role}</p>
|
|
145
|
+
<div>
|
|
146
|
+
{member.email && (
|
|
147
|
+
<a href={`mailto:${member.email}`} onclick="event.stopPropagation()">Email</a>
|
|
148
|
+
)}
|
|
149
|
+
{member.linkedIn && (
|
|
150
|
+
<a href={member.linkedIn} target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()">LinkedIn</a>
|
|
151
|
+
)}
|
|
152
|
+
{member.twitter && (
|
|
153
|
+
<a href={member.twitter} target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()">Twitter</a>
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
</a>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
> **Styling note:** TeamMemberCard styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Team Member Card.
|
|
160
|
+
|
|
161
|
+
Key details:
|
|
162
|
+
- Inline SVG icons — no icon library dependency
|
|
163
|
+
- Fallback initial letter when no photo
|
|
164
|
+
- Social links use `onclick="event.stopPropagation()"` to prevent navigation to profile when clicking social icons
|
|
165
|
+
|
|
166
|
+
### 3. Team Directory Page (`src/pages/team/index.astro`)
|
|
167
|
+
|
|
168
|
+
```astro
|
|
169
|
+
---
|
|
170
|
+
import Layout from "../../layouts/Layout.astro";
|
|
171
|
+
import TeamMemberCard from "../../components/TeamMemberCard.astro";
|
|
172
|
+
import { queryTeamMembers, groupByDepartment } from "../../lib/team";
|
|
173
|
+
|
|
174
|
+
const members = await queryTeamMembers();
|
|
175
|
+
const departments = groupByDepartment(members);
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
<Layout title="Our Team">
|
|
179
|
+
<main>
|
|
180
|
+
<div>
|
|
181
|
+
<h1>Our Team</h1>
|
|
182
|
+
|
|
183
|
+
{[...departments.entries()].map(([dept, deptMembers]) => (
|
|
184
|
+
<section>
|
|
185
|
+
<h2>{dept}</h2>
|
|
186
|
+
<div>
|
|
187
|
+
{deptMembers.map((member) => (
|
|
188
|
+
<TeamMemberCard member={member} />
|
|
189
|
+
))}
|
|
190
|
+
</div>
|
|
191
|
+
</section>
|
|
192
|
+
))}
|
|
193
|
+
|
|
194
|
+
{members.length === 0 && (
|
|
195
|
+
<p>No team members found.</p>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
</main>
|
|
199
|
+
</Layout>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 4. Member Profile Page (`src/pages/team/[slug].astro`)
|
|
203
|
+
|
|
204
|
+
```astro
|
|
205
|
+
---
|
|
206
|
+
import Layout from "../../layouts/Layout.astro";
|
|
207
|
+
import { getMemberBySlug } from "../../lib/team";
|
|
208
|
+
|
|
209
|
+
const { slug } = Astro.params;
|
|
210
|
+
const member = await getMemberBySlug(slug!);
|
|
211
|
+
if (!member) return Astro.redirect("/404");
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
<Layout title={member.name}>
|
|
215
|
+
<main>
|
|
216
|
+
<div>
|
|
217
|
+
<a href="/team">
|
|
218
|
+
← Back to team
|
|
219
|
+
</a>
|
|
220
|
+
|
|
221
|
+
<div>
|
|
222
|
+
{member.photo && (
|
|
223
|
+
<div>
|
|
224
|
+
<img
|
|
225
|
+
src={member.photo}
|
|
226
|
+
alt={member.name}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
<div>
|
|
231
|
+
<h1>{member.name}</h1>
|
|
232
|
+
<p>{member.role}</p>
|
|
233
|
+
{member.department && (
|
|
234
|
+
<p>{member.department}</p>
|
|
235
|
+
)}
|
|
236
|
+
<div>
|
|
237
|
+
{member.email && (
|
|
238
|
+
<a href={`mailto:${member.email}`}>
|
|
239
|
+
{member.email}
|
|
240
|
+
</a>
|
|
241
|
+
)}
|
|
242
|
+
{member.linkedIn && (
|
|
243
|
+
<a href={member.linkedIn} target="_blank" rel="noopener noreferrer">
|
|
244
|
+
LinkedIn
|
|
245
|
+
</a>
|
|
246
|
+
)}
|
|
247
|
+
{member.twitter && (
|
|
248
|
+
<a href={member.twitter} target="_blank" rel="noopener noreferrer">
|
|
249
|
+
Twitter
|
|
250
|
+
</a>
|
|
251
|
+
)}
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
{member.bio && (
|
|
257
|
+
<article set:html={member.bio} />
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
</main>
|
|
261
|
+
</Layout>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Seed with Images
|
|
265
|
+
|
|
266
|
+
> **Do not skip:** Team members without photos appear as initial-letter placeholders.
|
|
267
|
+
> Always ask the user if they want to generate headshot images before moving on.
|
|
268
|
+
|
|
269
|
+
After seeding team members via MCP, generate headshot images following `../../shared/IMAGE_GENERATION.md` and `CMS_FOUNDATIONS.md` → "MCP Seeding with Images".
|
|
270
|
+
|
|
271
|
+
**Prompt template:**
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Professional headshot portrait of [NAME], [ROLE] at [BUSINESS NAME]. [BRAND AESTHETIC]. Warm studio lighting, neutral background with [BRAND COLORS]. No text, no watermarks
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Patch field:** `photo`
|
|
278
|
+
|
|
279
|
+
Example: after generating and importing the image, patch the team member item:
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
CallWixSiteAPI: PATCH /wix-data/v2/items/{memberId}
|
|
283
|
+
body: {
|
|
284
|
+
"dataCollectionId": "Team",
|
|
285
|
+
"dataItem": {
|
|
286
|
+
"data": {
|
|
287
|
+
"photo": "<wixstatic-url>"
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Testing
|
|
294
|
+
|
|
295
|
+
1. Create a "Team" collection in the Wix dashboard → CMS with the schema above
|
|
296
|
+
2. Add 4+ members across 2+ departments, with photos and social links
|
|
297
|
+
3. Run `npx @wix/cli dev`
|
|
298
|
+
4. `/team` — shows members grouped by department
|
|
299
|
+
5. Click a member — profile page with photo, bio, and social links
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Install a Wix app onto a site
|
|
2
|
+
|
|
3
|
+
Reference for the orchestrator. Invoked from `SKILL.md` Step 2 once per app entry across the loaded packs' `apps[*]`. Can't be a bash script — the install goes through the Wix MCP tool `CallWixSiteAPI`, not over the wire from the local shell.
|
|
4
|
+
|
|
5
|
+
## Inputs
|
|
6
|
+
|
|
7
|
+
- `siteId` — from `wix.config.json`.
|
|
8
|
+
- `appName` — e.g. `wix-stores`, `wix-forms`, `wix-blog`. Resolved to `appDefId` via `<SKILL_ROOT>/references/commands/known-apps.json`. Unknown name → fail fast (`"Unknown app: {appName}. Known: [list]. Add it to references/commands/known-apps.json before retrying."`); do not guess.
|
|
9
|
+
- `mcpPrefix` — captured in Step 0 (the discovered Wix MCP prefix; see `<SKILL_ROOT>/references/shared/MCP_PREFIX.md`).
|
|
10
|
+
|
|
11
|
+
## Call
|
|
12
|
+
|
|
13
|
+
Wrap in `date -u` captures so timing flows into `run.json.phases[]` as `{ phase: "app-install-{appName}", seconds: <duration> }`.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
{mcpPrefix}CallWixSiteAPI
|
|
17
|
+
siteId: {siteId}
|
|
18
|
+
url: https://www.wixapis.com/apps-installer-service/v1/app-instance/install
|
|
19
|
+
method: POST
|
|
20
|
+
body: (a JSON object — NOT a string)
|
|
21
|
+
{
|
|
22
|
+
"tenant": { "tenantType": "SITE", "id": "{siteId}" },
|
|
23
|
+
"appInstance": { "appDefId": "{appDefId}", "enabled": true }
|
|
24
|
+
}
|
|
25
|
+
reason: Install {appName} on the new headless site
|
|
26
|
+
sourceDocUrl: https://dev.wix.com/docs/api-reference/business-management/app-installation/skills/install-wix-apps
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The MCP tool requires explicit `url` / `method` / `body` / `reason` / `sourceDocUrl`. Do NOT pass `endpoint:`. Do NOT stringify `body` — pass it as a JSON object. (The Step 0 MCP bootstrap — see `<SKILL_ROOT>/references/commands/mcp-bootstrap.md` — preloads the `CallWixSiteAPI` schema so the orchestrator and subagents both see the right shape.)
|
|
30
|
+
|
|
31
|
+
## Strict-then-recover
|
|
32
|
+
|
|
33
|
+
| Outcome | Action |
|
|
34
|
+
|---|---|
|
|
35
|
+
| 2xx | Done. |
|
|
36
|
+
| 4xx / 5xx with parseable body | Enter recover loop (max 1 retry): `{mcpPrefix}SearchWixRESTDocumentation` for `install app instance`, then `{mcpPrefix}ReadFullDocsMethodSchema` on the top match, then retry with the discovered shape. |
|
|
37
|
+
| Recovery succeeds | Append to `run.json.commandDrift[]`: `{ command: "install-app", primaryError: { status, body }, recoveredWith: { endpoint, bodyPatch }, suggestedFix: "Update references/commands/install-app.md body template" }`. |
|
|
38
|
+
| Both paths fail | Surface the original response body and the doc link to the user. Do not loop further. |
|
|
39
|
+
|
|
40
|
+
## Why this is a reference doc, not a script
|
|
41
|
+
|
|
42
|
+
The body shape and the strict-then-recover ladder are the parts that need to stay frozen across runs. Encoding them in a markdown reference the orchestrator reads (instead of a slash command the orchestrator dispatches) keeps the source of truth inside the skill and removes the round-trip through a slash-command shim.
|