From f0c283428082e8b5dbeaeb5dd4ba24fc7074edf7 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 19 Sep 2025 22:05:03 +0100 Subject: [PATCH 1/8] feat(ui): add contextual help button to view headers - Create HelpButton component with contextual help content for all 7 view states - Position help button on right side of ViewHeader component - Style as round button with white border, transparent background, and "i" icon - Provide specific guidance for Dashboard, Actions, Resources, Categories, Responses, and Email views --- src/lib/components/layouts/ViewHeader.svelte | 13 ++- src/lib/components/ui/HelpButton.svelte | 99 ++++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/lib/components/ui/HelpButton.svelte diff --git a/src/lib/components/layouts/ViewHeader.svelte b/src/lib/components/layouts/ViewHeader.svelte index e0e0573..426f5a0 100644 --- a/src/lib/components/layouts/ViewHeader.svelte +++ b/src/lib/components/layouts/ViewHeader.svelte @@ -1,4 +1,6 @@
-

{title}

+

{title}

- {#if onclick} - - {/if} +
+ + {#if onclick} + + {/if} +
diff --git a/src/lib/components/ui/HelpButton.svelte b/src/lib/components/ui/HelpButton.svelte new file mode 100644 index 0000000..32ff374 --- /dev/null +++ b/src/lib/components/ui/HelpButton.svelte @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file From d5a7333cee278956c135ff61a195befd8279382f Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 19 Sep 2025 22:49:01 +0100 Subject: [PATCH 2/8] feat(help): enhance help system with rich content and modal support - Replace simple tooltip help with comprehensive modal-based help system - Add HelpModal component extending InfoModal pattern with screenshot support - Create structured help content JSON with contextual guidance for all 7 view states - Add TypeScript types and content service with caching and fallback support - Implement screenshot placeholder system ready for real images - Simplify HelpButton to single-mode interaction with loading states --- src/lib/components/ui/HelpButton.svelte | 68 +++++---- src/lib/components/ui/HelpModal.svelte | 126 ++++++++++++++++ src/lib/services/helpContent.ts | 59 ++++++++ src/lib/types/help.ts | 28 ++++ static/help/content/help-content.json | 191 ++++++++++++++++++++++++ static/help/screenshots/.gitkeep | 18 +++ 6 files changed, 458 insertions(+), 32 deletions(-) create mode 100644 src/lib/components/ui/HelpModal.svelte create mode 100644 src/lib/services/helpContent.ts create mode 100644 src/lib/types/help.ts create mode 100644 static/help/content/help-content.json create mode 100644 static/help/screenshots/.gitkeep diff --git a/src/lib/components/ui/HelpButton.svelte b/src/lib/components/ui/HelpButton.svelte index 32ff374..476ed0c 100644 --- a/src/lib/components/ui/HelpButton.svelte +++ b/src/lib/components/ui/HelpButton.svelte @@ -1,17 +1,23 @@ - + - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/lib/components/ui/HelpModal.svelte b/src/lib/components/ui/HelpModal.svelte new file mode 100644 index 0000000..203db4b --- /dev/null +++ b/src/lib/components/ui/HelpModal.svelte @@ -0,0 +1,126 @@ + + + { + if (show && e.key === 'Escape') { + onclose(); + } + }} +/> + +{#if show && helpContent} + { + if (event.currentTarget === event.target) { + onclose(); + } + }} + > + + +{/if} + + \ No newline at end of file diff --git a/src/lib/services/helpContent.ts b/src/lib/services/helpContent.ts new file mode 100644 index 0000000..1eadda9 --- /dev/null +++ b/src/lib/services/helpContent.ts @@ -0,0 +1,59 @@ +import type { HelpContent, HelpContentMap, HelpContextKey } from '$lib/types/help'; + +let helpContentCache: HelpContentMap | null = null; + +export async function loadHelpContent(): Promise { + if (helpContentCache) { + return helpContentCache; + } + + try { + const response = await fetch('/help/content/help-content.json'); + if (!response.ok) { + throw new Error(`Failed to load help content: ${response.statusText}`); + } + + helpContentCache = await response.json(); + return helpContentCache as HelpContentMap; + } catch (error) { + console.error('Error loading help content:', error); + // Return fallback content + return getFallbackHelpContent(); + } +} + +export async function getHelpContent(contextKey: HelpContextKey): Promise { + const allContent = await loadHelpContent(); + return allContent[contextKey] || getFallbackContent(contextKey); +} + +function getFallbackContent(contextKey: HelpContextKey): HelpContent { + return { + title: 'Help', + sections: [ + { + text: `Get guidance for using this section of your workplace passport. Context: ${contextKey}` + } + ] + }; +} + +function getFallbackHelpContent(): HelpContentMap { + const contexts: HelpContextKey[] = [ + 'dashboard-no-profile', + 'dashboard-profile', + 'actions', + 'resources', + 'categories', + 'responses', + 'email' + ]; + + const fallbackContent: Partial = {}; + + for (const context of contexts) { + fallbackContent[context] = getFallbackContent(context); + } + + return fallbackContent as HelpContentMap; +} \ No newline at end of file diff --git a/src/lib/types/help.ts b/src/lib/types/help.ts new file mode 100644 index 0000000..c006948 --- /dev/null +++ b/src/lib/types/help.ts @@ -0,0 +1,28 @@ +export interface HelpScreenshot { + src: string; + alt: string; + caption?: string; +} + +export interface HelpSection { + heading?: string; + text: string; + screenshot?: HelpScreenshot; + callouts?: string[]; +} + +export interface HelpContent { + title: string; + sections: HelpSection[]; +} + +export type HelpContextKey = + | 'dashboard-no-profile' + | 'dashboard-profile' + | 'actions' + | 'resources' + | 'categories' + | 'responses' + | 'email'; + +export type HelpContentMap = Record; \ No newline at end of file diff --git a/static/help/content/help-content.json b/static/help/content/help-content.json new file mode 100644 index 0000000..c886845 --- /dev/null +++ b/static/help/content/help-content.json @@ -0,0 +1,191 @@ +{ + "dashboard-no-profile": { + "title": "Getting Started with Your Workplace Passport", + "sections": [ + { + "heading": "Welcome to Workwise", + "text": "Your Digital Workplace Passport helps you document and share your workplace needs with your line manager, promoting a more inclusive work environment.", + "screenshot": { + "src": "/help/screenshots/dashboard-no-profile.png", + "alt": "Dashboard showing profile selection dropdown", + "caption": "Select your profile to get started" + } + }, + { + "heading": "Getting Started", + "text": "To begin using your workplace passport:", + "callouts": [ + "Select your profile from the dropdown menu", + "Explore the category tiles to see available topics", + "Click on any category to start adding your responses", + "Use the email button to share your responses with your line manager" + ] + } + ] + }, + "dashboard-profile": { + "title": "Your Workplace Passport Dashboard", + "sections": [ + { + "heading": "Dashboard Overview", + "text": "Your dashboard shows category tiles representing different workplace topics. Each tile displays your progress and allows you to navigate to specific areas.", + "screenshot": { + "src": "/help/screenshots/dashboard-with-profile.png", + "alt": "Dashboard with category tiles showing user progress", + "caption": "Dashboard showing your progress across different categories" + } + }, + { + "heading": "Using the Dashboard", + "text": "Here's how to navigate your workplace passport:", + "callouts": [ + "Click on any category tile to explore questions in that area", + "View your response progress on each tile", + "Access your actions and resources from the dedicated tiles", + "Use the email button in the header to share your responses" + ] + }, + { + "heading": "What's Next?", + "text": "Start by exploring categories that are most relevant to your role and workplace needs. You can always come back to add more responses later." + } + ] + }, + "actions": { + "title": "Managing Your Actions", + "sections": [ + { + "heading": "About Actions", + "text": "Actions are personal plans you create to help address workplace challenges. They help you track your progress and can be shared with your line manager.", + "screenshot": { + "src": "/help/screenshots/actions-list.png", + "alt": "Actions list showing personal action items", + "caption": "Your personal actions list with progress tracking" + } + }, + { + "heading": "Working with Actions", + "text": "You can manage your actions in several ways:", + "callouts": [ + "Add new actions using the 'Add Action' button", + "Mark actions as complete when finished", + "Edit existing actions to update progress", + "Set actions as public/private to control sharing", + "Track your overall progress over time" + ] + }, + { + "heading": "Sharing Actions", + "text": "Public actions can be included in emails to your line manager, helping them understand your proactive approach to workplace challenges." + } + ] + }, + "resources": { + "title": "Workplace Resources", + "sections": [ + { + "heading": "Available Resources", + "text": "Resources are helpful materials, links, and information related to workplace support and neurodivergent needs.", + "screenshot": { + "src": "/help/screenshots/resources-view.png", + "alt": "Resources page showing available support materials", + "caption": "Browse available workplace support resources" + } + }, + { + "heading": "Using Resources", + "text": "Resources can help you:", + "callouts": [ + "Learn about workplace accommodations", + "Find support materials for specific challenges", + "Access external links and tools", + "Discover best practices for workplace inclusion", + "Get guidance on communicating your needs" + ] + } + ] + }, + "categories": { + "title": "Question Categories", + "sections": [ + { + "heading": "Exploring Categories", + "text": "Categories group related workplace topics and questions. Each category contains questions you can respond to about your workplace needs and preferences.", + "screenshot": { + "src": "/help/screenshots/categories-overview.png", + "alt": "Category view showing available questions", + "caption": "Questions within a category ready for your responses" + } + }, + { + "heading": "Responding to Questions", + "text": "When exploring categories:", + "callouts": [ + "Click on any question to add or edit your response", + "Provide detailed, honest responses about your needs", + "Set response visibility (public/private)", + "Save your responses to track your workplace passport", + "Return anytime to update or refine your answers" + ] + } + ] + }, + "responses": { + "title": "Your Responses", + "sections": [ + { + "heading": "Managing Your Responses", + "text": "Your responses document your workplace needs, preferences, and experiences. You have full control over what you share and with whom.", + "screenshot": { + "src": "/help/screenshots/response-editing.png", + "alt": "Response editing interface showing privacy controls", + "caption": "Edit responses and control their visibility" + } + }, + { + "heading": "Response Privacy", + "text": "You control the visibility of each response:", + "callouts": [ + "Public responses can be shared with your line manager", + "Private responses remain confidential to you", + "Change visibility settings anytime", + "Only you decide what information to share", + "Your privacy is always protected" + ] + }, + { + "heading": "Editing Responses", + "text": "You can update your responses as your needs change or as you gain new insights about your workplace preferences." + } + ] + }, + "email": { + "title": "Email Preview", + "sections": [ + { + "heading": "Sharing Your Workplace Passport", + "text": "The email preview shows a summary of your public responses and actions that can be shared with your line manager.", + "screenshot": { + "src": "/help/screenshots/email-preview.png", + "alt": "Email preview showing formatted workplace passport summary", + "caption": "Preview your workplace passport before sharing" + } + }, + { + "heading": "Before Sending", + "text": "Review the email content carefully:", + "callouts": [ + "Check that all information accurately represents your needs", + "Verify only public responses and actions are included", + "Ensure the tone and content feel appropriate", + "Add any additional context if needed", + "Remember you control when and what to share" + ] + }, + { + "heading": "Sending the Email", + "text": "When you're ready, the email will be sent to your line manager with a professional summary of your workplace passport, helping facilitate productive conversations about your needs." + } + ] + } +} \ No newline at end of file diff --git a/static/help/screenshots/.gitkeep b/static/help/screenshots/.gitkeep new file mode 100644 index 0000000..b2d1f03 --- /dev/null +++ b/static/help/screenshots/.gitkeep @@ -0,0 +1,18 @@ +# Screenshots Directory + +This directory contains screenshots for the help system. + +Current placeholders: +- dashboard-no-profile.png (coming soon) +- dashboard-with-profile.png (coming soon) +- actions-list.png (coming soon) +- resources-view.png (coming soon) +- categories-overview.png (coming soon) +- response-editing.png (coming soon) +- email-preview.png (coming soon) + +Screenshots should be: +- Consistent viewport size +- Use existing app styling +- Include realistic but privacy-safe demo data +- Highlight important UI elements where relevant \ No newline at end of file From a606293925130fd64d16829d630e210b7c0881c3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 19 Sep 2025 23:05:40 +0100 Subject: [PATCH 3/8] style(layout): improve header padding consistency with responsive design - Update header-content to use responsive padding (sm:px-2 lg:px-5) - Change view-header from p-3 to responsive py-2 sm:px-2 lg:px-5 - Remove redundant px-2 from header-left for cleaner layout - Ensure consistent horizontal alignment across header components --- src/app.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app.css b/src/app.css index 8db1515..4c6dcdd 100644 --- a/src/app.css +++ b/src/app.css @@ -110,11 +110,11 @@ h3 { } .header-content { - @apply flex h-20 w-full flex-row items-center justify-between px-2 sm:px-6 lg:px-8; + @apply flex h-20 w-full flex-row items-center justify-between sm:px-2 lg:px-5; } .header-left { - @apply flex items-center space-x-4 px-2; + @apply flex items-center space-x-4; } .view { @@ -122,7 +122,7 @@ h3 { } .view-header { - @apply bg-accent max-w-full p-3; + @apply bg-accent max-w-full py-2 sm:px-2 lg:px-5; /* height: 4.5rem; */ display: grid; grid-template-columns: 1fr auto; From 648d02097edfaa8afa3f4b8d250b1ab5460914e2 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 20 Sep 2025 17:42:21 +0100 Subject: [PATCH 4/8] style(layout): improve header padding consistency with responsive design --- src/app.css | 14 +++++++------- src/lib/components/layouts/Header.svelte | 8 ++++---- src/lib/components/layouts/ViewHeader.svelte | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/app.css b/src/app.css index 4c6dcdd..50eb3a8 100644 --- a/src/app.css +++ b/src/app.css @@ -61,7 +61,7 @@ h3 { } .dash-grid-2 { - @apply grid grid-cols-2 gap-8; + @apply mt-2 grid grid-cols-2; } .dash-tile-rect { @@ -70,7 +70,7 @@ h3 { } .dash-tile-square { - @apply card border-primary bg-base-100 my-1 rounded-lg border p-6 transition-shadow hover:shadow-md; + @apply card border-primary bg-base-100 mx-10 my-1 rounded-lg border p-3 transition-shadow hover:shadow-md; @apply flex flex-col items-center justify-center text-center; @apply cursor-pointer; /* Height constraints relative to .list-item */ @@ -87,8 +87,8 @@ h3 { } .footer { - @apply bg-primary text-primary-content h-20; - padding-bottom: env(safe-area-inset-bottom); + @apply bg-primary text-primary-content h-16; + /* padding-bottom: env(safe-area-inset-bottom); */ padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); } @@ -106,11 +106,11 @@ h3 { } .header-container-logo { - @apply rounded-xl bg-white p-2 shadow-sm; + @apply flex-shrink-0 rounded-xl bg-white p-2 shadow-sm; } .header-content { - @apply flex h-20 w-full flex-row items-center justify-between sm:px-2 lg:px-5; + @apply flex h-20 w-full flex-row items-center justify-between px-5; } .header-left { @@ -122,7 +122,7 @@ h3 { } .view-header { - @apply bg-accent max-w-full py-2 sm:px-2 lg:px-5; + @apply bg-accent h-14 max-w-full px-5; /* height: 4.5rem; */ display: grid; grid-template-columns: 1fr auto; diff --git a/src/lib/components/layouts/Header.svelte b/src/lib/components/layouts/Header.svelte index dc573ac..99e41af 100644 --- a/src/lib/components/layouts/Header.svelte +++ b/src/lib/components/layouts/Header.svelte @@ -25,12 +25,12 @@

Workwise

-

{version}

+
@@ -57,12 +57,12 @@ {/if} diff --git a/src/lib/components/layouts/ViewHeader.svelte b/src/lib/components/layouts/ViewHeader.svelte index 426f5a0..a835e79 100644 --- a/src/lib/components/layouts/ViewHeader.svelte +++ b/src/lib/components/layouts/ViewHeader.svelte @@ -13,9 +13,9 @@

{title}

- {#if onclick} {/if} +
From 03a2e2bdd65d699384aa35574cf4a0b713854f15 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 20 Sep 2025 18:02:34 +0100 Subject: [PATCH 5/8] refactor(help): centralize styles and add safety checks --- src/app.css | 15 +++++++++++++++ src/lib/components/ui/HelpButton.svelte | 7 +++++-- src/lib/components/ui/HelpModal.svelte | 17 ----------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/app.css b/src/app.css index 50eb3a8..891bf97 100644 --- a/src/app.css +++ b/src/app.css @@ -243,6 +243,21 @@ h3 { @apply mx-1 mb-2 w-full; } +.help-section { + border-bottom: 1px solid hsl(var(--bc) / 0.1); +} + +.help-section:last-child { + border-bottom: none; + padding-bottom: 0; +} + +.screenshot-container { + display: flex; + flex-direction: column; + align-items: center; +} + @media (max-width: 375px) { .dash-grid { @apply grid-cols-1; diff --git a/src/lib/components/ui/HelpButton.svelte b/src/lib/components/ui/HelpButton.svelte index 476ed0c..3299931 100644 --- a/src/lib/components/ui/HelpButton.svelte +++ b/src/lib/components/ui/HelpButton.svelte @@ -65,10 +65,13 @@ async function handleHelpClick() { if (isLoading) return; - + + const context = helpContext(); + if (!context) return; + isLoading = true; try { - helpContent = await getHelpContent(helpContext() as HelpContextKey); + helpContent = await getHelpContent(context as HelpContextKey); showHelpModal = true; } catch (error) { console.error('Failed to load help content:', error); diff --git a/src/lib/components/ui/HelpModal.svelte b/src/lib/components/ui/HelpModal.svelte index 203db4b..b2af3e0 100644 --- a/src/lib/components/ui/HelpModal.svelte +++ b/src/lib/components/ui/HelpModal.svelte @@ -107,20 +107,3 @@ {/if} - \ No newline at end of file From 84caa945abb50c8b382c1963f7c1f8516385beb3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 20 Sep 2025 18:08:12 +0100 Subject: [PATCH 6/8] fix(a11y): implement proper focus trap in help modal using inert --- src/lib/components/ui/HelpModal.svelte | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/components/ui/HelpModal.svelte b/src/lib/components/ui/HelpModal.svelte index b2af3e0..72ed6f7 100644 --- a/src/lib/components/ui/HelpModal.svelte +++ b/src/lib/components/ui/HelpModal.svelte @@ -17,15 +17,19 @@ $effect(() => { if (show && dialog) { previouslyFocused = document.activeElement as HTMLElement; + document.body.inert = true; dialog.showModal(); const autofocusElement = dialog.querySelector('[data-autofocus]'); if (autofocusElement) { autofocusElement.focus(); } - } else if (!show && dialog && previouslyFocused) { + } else if (!show && dialog) { dialog.close(); - previouslyFocused.focus(); - previouslyFocused = null; + document.body.inert = false; + if (previouslyFocused) { + previouslyFocused.focus(); + previouslyFocused = null; + } } }); From d5d42f93876ffa47e54fb15df292b021c2092196 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 20 Sep 2025 21:50:05 +0100 Subject: [PATCH 7/8] feat(nav): add clickable breadcrumbs replacing view headers --- src/app.css | 26 ++++ src/lib/components/layouts/ViewHeader.svelte | 3 +- src/lib/components/ui/Breadcrumb.svelte | 136 +++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/lib/components/ui/Breadcrumb.svelte diff --git a/src/app.css b/src/app.css index 891bf97..8d856a9 100644 --- a/src/app.css +++ b/src/app.css @@ -129,6 +129,7 @@ h3 { align-items: center; } + .view-layout { /* DASH */ @apply max-w-full justify-around space-y-4 px-2; @@ -258,6 +259,31 @@ h3 { align-items: center; } +.breadcrumb { + font-size: 1.25rem; + font-weight: 700; +} + +.breadcrumb-list { + @apply flex items-center space-x-2 m-0 p-0 list-none; +} + +.breadcrumb-item { + @apply flex items-center; +} + +.breadcrumb-link { + @apply text-accent-content hover:text-white transition-colors underline bg-transparent border-none cursor-pointer p-0 font-normal; +} + +.breadcrumb-current { + @apply text-accent-content font-bold; +} + +.breadcrumb-separator { + @apply text-accent-content/70 mx-2 font-normal; +} + @media (max-width: 375px) { .dash-grid { @apply grid-cols-1; diff --git a/src/lib/components/layouts/ViewHeader.svelte b/src/lib/components/layouts/ViewHeader.svelte index a835e79..18685ec 100644 --- a/src/lib/components/layouts/ViewHeader.svelte +++ b/src/lib/components/layouts/ViewHeader.svelte @@ -1,5 +1,6 @@
-

{title}

+
{#if onclick} diff --git a/src/lib/components/ui/Breadcrumb.svelte b/src/lib/components/ui/Breadcrumb.svelte new file mode 100644 index 0000000..90f8e85 --- /dev/null +++ b/src/lib/components/ui/Breadcrumb.svelte @@ -0,0 +1,136 @@ + + +{#if showBreadcrumbs} + +{/if} \ No newline at end of file From 5f489c34bc488670ff376ea93e069e43a23e901d Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 20 Sep 2025 22:05:33 +0100 Subject: [PATCH 8/8] perf(breadcrumb): optimize rendering and add null safety checks --- src/lib/components/ui/Breadcrumb.svelte | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/lib/components/ui/Breadcrumb.svelte b/src/lib/components/ui/Breadcrumb.svelte index 90f8e85..6f25d37 100644 --- a/src/lib/components/ui/Breadcrumb.svelte +++ b/src/lib/components/ui/Breadcrumb.svelte @@ -16,11 +16,12 @@ // Generate breadcrumb items based on current app state const breadcrumbItems = $derived(() => { - const items: BreadcrumbItem[] = []; const viewName = app.view.name; const listTable = app.list.table; const detailTable = app.detail.table; - const categoryFormat = app.list.category.format; + const categoryFormat = app.list.category?.format; + + const items: BreadcrumbItem[] = []; // Always start with Dashboard items.push({ @@ -60,7 +61,8 @@ label: categoryFormat, clickable: true, action: () => { - setList({ table: 'questions', category: app.list.category }); + const category = app.list.category || { raw: null, format: null }; + setList({ table: 'questions', category }); setViewName('list'); } }); @@ -106,14 +108,17 @@ return items; }); + // Cache the breadcrumb items to avoid multiple function calls + const items = $derived(breadcrumbItems()); + // Always show breadcrumbs (at minimum shows current page) - const showBreadcrumbs = $derived(breadcrumbItems().length > 0); + const showBreadcrumbs = $derived(items.length > 0); {#if showBreadcrumbs}