diff --git a/labs/SETUP.md b/labs/SETUP.md index 49d3b88..06d5a60 100644 --- a/labs/SETUP.md +++ b/labs/SETUP.md @@ -128,6 +128,17 @@ The server starts at: http://localhost:3000 ![NYC Masterclass homepage at localhost:3000](images/nyc-masterclass-home.png) +> **Note:** +> If you see a **403 error** when navigating to [http://localhost:3000](http://localhost:3000), check if a `.env` file exists in your project root. +> If not, create a `.env` file containing: +> +> ``` +> AEM_PAGES_URL = https://main--nycmasterclass--cloudadoption.aem.page +> ``` +> +> After saving this file, **restart your local development server** (`aem up`). + + --- ## Step 7: AI Coding Agent Setup diff --git a/labs/exercise1/images/how-to-insert-a-template-da.gif b/labs/exercise1/images/how-to-insert-a-template-da.gif new file mode 100644 index 0000000..87be9ce Binary files /dev/null and b/labs/exercise1/images/how-to-insert-a-template-da.gif differ diff --git a/labs/exercise1/images/select-template.png b/labs/exercise1/images/select-template.png new file mode 100644 index 0000000..e94ecd1 Binary files /dev/null and b/labs/exercise1/images/select-template.png differ diff --git a/labs/exercise1/images/slash-command-select-library.png b/labs/exercise1/images/slash-command-select-library.png new file mode 100644 index 0000000..494e6ed Binary files /dev/null and b/labs/exercise1/images/slash-command-select-library.png differ diff --git a/labs/exercise1/instructions.md b/labs/exercise1/instructions.md index 32d7857..fad1d22 100644 --- a/labs/exercise1/instructions.md +++ b/labs/exercise1/instructions.md @@ -134,13 +134,22 @@ In the page editor: ![Slash command menu in DA.live](images/slash-command-in-da.png) -2. Select **Templates** -3. Choose **Session** or **Lab** depending on what you are creating +2. Select **Library** + + ![Select Library option from the menu in DA.live](images/slash-command-select-library.png) + +3. Select **Templates** + + ![Select Templates option](images/select-template.png) + +4. Choose **Session** or **Lab** depending on what you are creating ![Session and Lab template options](images/use-session-lab-template.png) The template inserts a Hero block, content sections, and a Metadata block — all with placeholder text. + ![How to insert a template in DA](images/how-to-insert-a-template-da.gif) + > **Didn't find a template?** You can also insert individual blocks via the **Blocks Library**: > Type `/` → select **Blocks** → browse and insert any block with placeholder content pre-filled. > diff --git a/labs/exercise2/images/cards-block-1.gif b/labs/exercise2/images/cards-block-1.gif new file mode 100644 index 0000000..b84e900 Binary files /dev/null and b/labs/exercise2/images/cards-block-1.gif differ diff --git a/labs/exercise2/images/cards-block-2.gif b/labs/exercise2/images/cards-block-2.gif new file mode 100644 index 0000000..6821e72 Binary files /dev/null and b/labs/exercise2/images/cards-block-2.gif differ diff --git a/labs/exercise2/images/cards-block-3.png b/labs/exercise2/images/cards-block-3.png new file mode 100644 index 0000000..e6408c1 Binary files /dev/null and b/labs/exercise2/images/cards-block-3.png differ diff --git a/labs/exercise2/images/cards-block-4.png b/labs/exercise2/images/cards-block-4.png new file mode 100644 index 0000000..13a456e Binary files /dev/null and b/labs/exercise2/images/cards-block-4.png differ diff --git a/labs/exercise2/images/cards-block-5.png b/labs/exercise2/images/cards-block-5.png new file mode 100644 index 0000000..be4e589 Binary files /dev/null and b/labs/exercise2/images/cards-block-5.png differ diff --git a/labs/exercise2/images/cards-block-6.png b/labs/exercise2/images/cards-block-6.png new file mode 100644 index 0000000..bc70716 Binary files /dev/null and b/labs/exercise2/images/cards-block-6.png differ diff --git a/labs/exercise2/images/cards-block-7.png b/labs/exercise2/images/cards-block-7.png new file mode 100644 index 0000000..a5ad910 Binary files /dev/null and b/labs/exercise2/images/cards-block-7.png differ diff --git a/labs/exercise2/instructions.md b/labs/exercise2/instructions.md index 431153a..1561635 100644 --- a/labs/exercise2/instructions.md +++ b/labs/exercise2/instructions.md @@ -206,6 +206,8 @@ Add content to test the **existing** Cards block (default behavior). Use the **B 2. Insert **default Cards** (add a heading like "Default Cards" if you like). 3. Insert the **Cards block that includes the eyebrow** (italic label). Add a heading like **Cards with Eyebrow** above it — you'll use this when we add the eyebrow enhancement in Step 3. + ![Cards Block and Variants in DA.live](images/cards-block-1.gif) + You'll add List and View Switcher content later when we implement those variations (Steps 5 and 7). DA.live auto-saves. Click **Preview** to see the page on localhost, then continue to Step 2. --- @@ -220,6 +222,8 @@ You'll add List and View Switcher content later when we implement those variatio - Default Cards section showing cards in a grid - A second section with cards that have italic (eyebrow) text — still rendered as plain text for now + ![Cards Block and Variants in DA.live](images/cards-block-2.gif) + **Why?** The eyebrow enhancement isn't implemented yet. We'll add it in the next step. --- @@ -323,6 +327,7 @@ Add at the end of the file: - The italic text you authored is no longer displayed inline — it's been extracted into the eyebrow - Default Cards section is unaffected (no italic text = no eyebrow) + ![Cards Block and Variants in DA.live](images/cards-block-3.png) --- ## Step 5: Implement List Variation @@ -331,6 +336,8 @@ The list variation displays cards in a single column with centered text. Authors **Add content to test it**: In DA.live, open your cards-test page. Type `/` → Library → Blocks → insert **Cards (List)** (add a heading like "List Variation" if you like). Preview so the page has a section to verify in Step 6. + ![Cards Block and Variants in DA.live](images/cards-block-4.png) + ### Add List Styles **File**: `blocks/cards/cards.css` @@ -366,6 +373,7 @@ Add at the end of the file: - Cards are full width with full-width images - Text is centered +![Cards Block and Variants in DA.live](images/cards-block-5.png) --- ## Step 7: Implement View Switcher Variation @@ -374,6 +382,8 @@ Now let's add a variation that combines **JavaScript and CSS**. The view switche **Add content to test it**: In DA.live, open your cards-test page. Type `/` → Library → Blocks → insert **Cards (View Switcher)** (add a heading like "View Switcher" if you like). Preview so the page has a section to verify in Step 8. + ![Cards Block and Variants in DA.live](images/cards-block-6.png) + **This is different from list**: List is a fixed layout chosen by the author. View switcher gives the **end user** control over the layout. ### Update JavaScript @@ -481,6 +491,8 @@ Add the toolbar styles at the end of the file: - Click **Grid** → cards switch back to the grid layout - The eyebrow enhancement still works (italic text → eyebrow label) in both views +![Cards Block and Variants in DA.live](images/cards-block-7.png) + --- ## Step 9: Commit Your Changes diff --git a/labs/exercise3/images/exercise-3-2.png b/labs/exercise3/images/exercise-3-2.png new file mode 100644 index 0000000..8fe2eaf Binary files /dev/null and b/labs/exercise3/images/exercise-3-2.png differ diff --git a/labs/exercise3/images/exercise-3-3.png b/labs/exercise3/images/exercise-3-3.png new file mode 100644 index 0000000..0793161 Binary files /dev/null and b/labs/exercise3/images/exercise-3-3.png differ diff --git a/labs/exercise3/images/exercise-3-4.png b/labs/exercise3/images/exercise-3-4.png new file mode 100644 index 0000000..eff2875 Binary files /dev/null and b/labs/exercise3/images/exercise-3-4.png differ diff --git a/labs/exercise3/images/exercise_3_1.gif b/labs/exercise3/images/exercise_3_1.gif new file mode 100644 index 0000000..2a041ca Binary files /dev/null and b/labs/exercise3/images/exercise_3_1.gif differ diff --git a/labs/exercise3/instructions.md b/labs/exercise3/instructions.md index f266210..a92712a 100644 --- a/labs/exercise3/instructions.md +++ b/labs/exercise3/instructions.md @@ -19,8 +19,7 @@ - [Step 7: Create Test Page](#step-7-create-test-page) - [Step 8: Test Locally](#step-8-test-locally) - [Step 9: Test Error Handling](#step-9-test-error-handling) - - [Step 10: Optional - Understanding Worker Endpoints](#step-10-optional---understanding-worker-endpoints) - - [Step 11: Optional - Apply to Real Speakers Page](#step-11-optional---apply-to-real-speakers-page) + - [Step 10: Optional - Apply to Real Speakers Page](#step-10-optional---apply-to-real-speakers-page) - [Key Takeaways](#key-takeaways) --- @@ -59,7 +58,6 @@ git branch - How to fetch data from external sources in blocks - How Sheets convert to JSON in DA.live -- How to use Worker endpoints for data transformation - How to handle async operations and errors in blocks --- @@ -74,7 +72,7 @@ In Exercise 2, you built blocks where authors manually create each card. But wha **Manual authoring doesn't scale**. Dynamic blocks solve this by fetching data from external sources. **The pattern**: -- Content lives in structured data (Sheet, API, Worker) +- Content lives in structured data (Sheet, API) - Block fetches and renders that data - Update the data once → all pages update automatically @@ -96,29 +94,7 @@ In Exercise 2, you built blocks where authors manually create each card. But wha 5. When sheet updates, all pages show new data ``` -**Alternative flow with Workers**: -``` -1. Data in external system (database, API, query-index.json) -2. Worker fetches and transforms data -3. Worker returns JSON -4. Block fetches from Worker endpoint -5. Block renders cards -``` - -**Example - Featured content from query index**: -``` -1. Worker fetches query-index.json -2. Worker filters pages tagged "featured" -3. Worker sorts by publishedDate (newest first) -4. Worker limits to 3 results -5. Dynamic Cards block renders featured articles -``` - -**Why use Workers?** -- Keep API keys server-side (secure) -- Transform/filter data before sending to client -- Combine multiple data sources -- Cache expensive operations +In this exercise we'll use a Sheet-based `.json` endpoint that lives as content in DA — but the block itself doesn't care where the data comes from. Any endpoint that returns the same JSON shape will work, whether it's a Cloudflare Worker, a third-party API, or a custom backend. Swap the URL and everything else stays the same. **Reference**: [Integrations](https://www.aem.live/developer/integrations) @@ -169,13 +145,15 @@ To avoid conflicts with 50 participants, you'll create your own personal speaker **In DA.live**: -1. Navigate to https://da.live/#/cloudadoption/nycmasterclass/speakers +1. Navigate to https://da.live/sheet#/cloudadoption/nycmasterclass/speakers 2. This is the master speakers.json with 6 Adobe experts -3. Click the **3-dot menu** → **Copy** +3. Go back to the root folder and select the `speakers` file and click `copy` 4. Navigate to `/drafts/jsmith/` (your folder) 5. **Paste** the speakers file 6. Rename it to `speakers` (keep it as a .json file) + ![Copy & Paste Speakers Sheet](images/exercise_3_1.gif) + **Verify**: You should now have `/drafts/jsmith/speakers.json` --- @@ -230,7 +208,7 @@ Copy this code: * Fetches speaker data and renders cards dynamically * * Author provides (in block): - * - Row 1: Data source URL (sheet.json or worker endpoint) + * - Row 1: Data source URL (sheet.json endpoint) */ export default async function decorate(block) { // Extract data source URL from block content @@ -450,6 +428,7 @@ This page demonstrates fetching speaker data dynamically from JSON. |---------------| | /drafts/jsmith/speakers.json | ``` + ![Dynamic Cards Example](images/exercise-3-2.png) **Important**: Use YOUR actual path (replace `jsmith` with your first initial + last name). @@ -475,6 +454,8 @@ DA.live auto-saves. Click **Preview** to see the page on localhost. - Verify your name, title, company, and bio display correctly - Check that your avatar image loads + ![Dynamic Cards Example](images/exercise-3-3.png) + **Test the data flow**: 1. Keep the page open at `http://localhost:3000/drafts/jsmith/speakers-test` 2. Go to DA.live and open `/drafts/jsmith/speakers` @@ -501,6 +482,7 @@ Edit your `/drafts/jsmith/speakers-test` page in DA.live, change the URL to: Refresh localhost. You should see: "Error loading speakers: [error message]" with red background. +![Dynamic Cards Error Handling Example](images/exercise-3-4.png) **Test 2 - No URL**: Remove the URL row entirely: @@ -526,72 +508,7 @@ Refresh. Speaker cards should display again. --- -## Step 10: Optional - Understanding Worker Endpoints - -Workers provide a powerful middleware layer between data sources and blocks. - -**Why use Workers?** -- **Security**: Keep API keys server-side (never expose in client code) -- **Transformation**: Filter, sort, aggregate, or enrich data server-side -- **Combination**: Merge multiple data sources into one response -- **Caching**: Cache expensive API calls at the edge -- **Rate Limiting**: Protect external APIs from high traffic - -**Example Worker flow**: -``` -1. Block fetches: https://worker.example.com/speakers -2. Worker fetches speakers.json internally -3. Worker adds computed fields (e.g., initials for avatars) -4. Worker sorts alphabetically by name -5. Worker filters by session category -6. Worker returns transformed JSON -7. Block renders without knowing about transformations -``` - -**Example Worker code** (Cloudflare Workers): -```javascript -export default { - async fetch(request) { - // Fetch the speakers sheet - const sheetUrl = 'https://main--nycmasterclass--cloudadoption.aem.page/speakers.json'; - const response = await fetch(sheetUrl); - const data = await response.json(); - - // Transform: Add initials, sort alphabetically - data.data = data.data - .map(speaker => ({ - ...speaker, - Initials: speaker.Name.split(' ').map(n => n[0]).join('') - })) - .sort((a, b) => a.Name.localeCompare(b.Name)); - - return new Response(JSON.stringify(data), { - headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', - 'Cache-Control': 'public, max-age=300' // Cache for 5 minutes - } - }); - } -} -``` - -**Key benefit**: To use a Worker instead of direct sheet access, authors just change the URL in the block: -``` -| Dynamic Cards | -|---------------| -| https://worker.example.com/speakers | -``` - -No block code changes needed! The JSON structure stays the same. - -**When you'll use this**: Exercise 6 covers form submissions to Slack via Workers. You'll deploy a real Worker endpoint then. - -**Reference**: [Integrations - Edge Workers](https://www.aem.live/developer/integrations) - ---- - -## Step 11: Optional - Apply to Real Speakers Page +## Step 10: Optional - Apply to Real Speakers Page Now that your dynamic-cards block is working, you can see how it would complete the real `/speakers` page. @@ -675,14 +592,14 @@ try { Note: In Exercise 4, you'll learn how query-index.json works and build a dedicated block for it. **Use Case 2: E-commerce Product Catalog** -- Product data in database -- Worker fetches, filters by category, adds pricing -- Block displays with live inventory status +- Product data maintained in a Sheet +- Block fetches and renders product cards with pricing +- Update the sheet → all catalog pages update automatically **Use Case 3: News/Blog Feeds** -- Articles in CMS -- Worker aggregates from multiple sources -- Block displays with filtering by topic +- Articles indexed via query-index.json +- Block fetches and displays recent articles +- Filter by topic or tag using query parameters --- @@ -690,7 +607,7 @@ Note: In Exercise 4, you'll learn how query-index.json works and build a dedicat - Sheets automatically convert to JSON endpoints in DA.live - Dynamic blocks fetch data instead of decorating authored content -- Workers provide secure middleware for data transformation +- The same block works with any JSON endpoint - Always handle loading states and errors - Same JSON structure as Sheets - reusable patterns - Performance: consider placement (below fold preferred) @@ -708,7 +625,7 @@ Note: In Exercise 4, you'll learn how query-index.json works and build a dedicat - [ ] Tested editing sheet data - changes reflected on refresh - [ ] Tested error handling (invalid URL, no URL, restore) - [ ] Tested in Chrome DevTools responsive view (desktop and mobile) -- [ ] Understand when to use Workers vs direct sheet fetch +- [ ] Understand the sheet-to-JSON data flow - [ ] Committed and pushed block code changes --- diff --git a/labs/exercise4/images/query-index.png b/labs/exercise4/images/query-index.png new file mode 100644 index 0000000..e737132 Binary files /dev/null and b/labs/exercise4/images/query-index.png differ diff --git a/labs/exercise4/images/search-block.png b/labs/exercise4/images/search-block.png new file mode 100644 index 0000000..146220d Binary files /dev/null and b/labs/exercise4/images/search-block.png differ diff --git a/labs/exercise4/images/search_demo.gif b/labs/exercise4/images/search_demo.gif new file mode 100644 index 0000000..cbec059 Binary files /dev/null and b/labs/exercise4/images/search_demo.gif differ diff --git a/labs/exercise4/instructions.md b/labs/exercise4/instructions.md index 969bc7a..e00b2bb 100644 --- a/labs/exercise4/instructions.md +++ b/labs/exercise4/instructions.md @@ -152,8 +152,11 @@ Look for your `/labs/jsmith/my-session` path in the `data` array. **Validate index definition**: [Index Admin](https://tools.aem.live/tools/index-admin/index.html?org=cloudadoption&site=nycmasterclass) — use this tool to fetch and validate the index configuration (include/exclude paths, properties) for this org/site. + ![Query Index Example](images/query-index.png) + > **Note**: Index updates can take a few minutes after publishing. If you don't see your page yet, continue — it will be there by the time you test. + --- ## Step 2: Look at the Block Collection Reference @@ -614,6 +617,8 @@ Add this content: The URL row is optional — if omitted, the block defaults to `/query-index.json`. Authors can point the block at any JSON endpoint that returns `{ data: [...] }`. + ![search Example](images/search-block.png) + DA.live auto-saves. Click **Preview** to see the page on localhost. --- @@ -646,6 +651,8 @@ Results render as Cards block cards — same dark cards with hover effect you sa **Escape key**: Press Escape while the input is focused to clear the search and results. + ![Search demo](images/search_demo.gif) + --- ## Step 8: Test Edge Cases diff --git a/labs/exercise8/instructions.md b/labs/exercise8/instructions.md index 3e848ea..dfed26f 100644 --- a/labs/exercise8/instructions.md +++ b/labs/exercise8/instructions.md @@ -523,7 +523,7 @@ Ask other participants to share their site URLs and compare. A working implementation of this exercise is available for the site `ukhalid-mc`. -- Site: [https://main--ukhalid-mc--cloudadoption.aem.page/](https://main--ukhalid-mc--cloudadoption.aem.page/) +- Site: [https://answers--ukhalid-mc--cloudadoption.aem.page/](https://answers--ukhalid-mc--cloudadoption.aem.page/) - Metadata sheet: [https://da.live/sheet#/cloudadoption/ukhalid-mc/metadata](https://da.live/sheet#/cloudadoption/ukhalid-mc/metadata) ---