diff --git a/TUTORIAL.md b/TUTORIAL.md
new file mode 100644
index 0000000..f4808a0
--- /dev/null
+++ b/TUTORIAL.md
@@ -0,0 +1,285 @@
+# Workwise User Guide
+
+**Your Digital Workplace Passport**
+
+---
+
+## Table of Contents
+
+1. [Getting Started](#getting-started)
+2. [Dashboard Overview](#dashboard-overview)
+3. [Managing Your Responses](#managing-your-responses)
+4. [Working with Categories](#working-with-categories)
+5. [Managing Actions](#managing-actions)
+6. [Accessing Resources](#accessing-resources)
+7. [Sharing Your Workplace Passport](#sharing-your-workplace-passport)
+8. [Privacy and Control](#privacy-and-control)
+
+---
+
+## Getting Started
+
+### Welcome to Workwise
+
+Your Digital Workplace Passport helps you document and share your workplace needs with your line manager, promoting a more inclusive work environment.
+
+
+
+
+
+
Select your profile to get started
+
+
+### First Steps
+
+To begin using your workplace passport:
+
+1. **Sign in or create an account** - Enter your email address to receive a magic link for secure, password-free authentication
+2. **Check your email** - Click the magic link sent to your inbox to access your account
+3. **Select your profile** from the dropdown menu (if you have multiple profiles)
+4. **Explore the dashboard** - organized into three sections: Actions, Categories, and Resources
+5. **Click on any category tile** to start adding your responses
+6. **Use the email button** in the header to share your responses with your line manager
+7. **Access help anytime** by clicking the 'i' button in the footer
+
+---
+
+## Dashboard Overview
+
+### Understanding Your Dashboard
+
+Your dashboard is organized into three main sections: **Actions** (personal plans), **Categories** (workplace topics), and **Resources** (support materials). Each section contains tiles that allow you to navigate to specific areas.
+
+
+
+
+
+
Dashboard showing Actions, Categories, and Resources sections
+
+
+### Navigating the Dashboard
+
+#### Actions Section
+View and manage your personal action items that help you address workplace challenges.
+
+#### Categories Section
+Click category tiles to explore questions and add responses about your workplace needs and preferences.
+
+#### Resources Section
+Access helpful workplace support materials and external links.
+
+### Dashboard Controls
+
+- **Email button** (in header): Share your public responses with your line manager
+- **Help button** (in footer): Access context-sensitive help for your current view
+- **Font size control** (in footer): Adjust text size for comfortable reading
+- **Terms and Privacy** (in footer): Review terms of use and privacy policy
+
+---
+
+## Managing Your Responses
+
+### About Responses
+
+Your responses document your workplace needs, preferences, and experiences. You have full control over what you share and with whom. Access your responses by clicking on a question from any category.
+
+
+
+
+
+
Edit responses and control their visibility
+
+
+### Response Options
+
+When viewing a question, you can:
+
+- **Provide a detailed response** explaining your needs or preferences
+- **Mark the question as 'Not Applicable'** if it doesn't apply to you
+- **Skip the question** to answer it later
+- **Edit or update your response** anytime
+- **Delete your response** if you no longer want it saved
+
+### Response Privacy
+
+You control the visibility of each response:
+
+- **Public responses** can be shared with your line manager via email
+- **Private responses** remain confidential to you
+- **Change visibility settings** anytime using the toggle
+- **Only you decide** what information to share
+- **Your privacy is always protected**
+
+### Creating Actions from Responses
+
+Once you've provided a response, you can create personal action items to address workplace challenges:
+
+- **Actions can only be created after you've answered a question**
+- Click **'Add Action'** to create a personal plan linked to this question
+- **Edit or delete actions** anytime to track your progress
+- Set actions as **Active/Inactive** to show current priorities
+- **Control action visibility** (public/private) to decide what to share
+- **Public actions appear in emails** to your line manager
+
+### Navigation
+
+Use the **Back button** in the header to return to the previous view. Your changes are saved automatically as you work.
+
+---
+
+## Working with Categories
+
+### Exploring Categories
+
+Categories group related workplace topics and questions. Each category contains questions you can respond to about your workplace needs and preferences. Click any category tile from the Categories section on your dashboard to begin.
+
+
+
+
+
+
Questions within a category - checkmark shows answered, question mark shows needs attention
+
+
+### Understanding Question Status
+
+- **Question mark (?)** - Questions that require your attention
+- **Checkmark (✓)** - Questions that are already answered or skipped
+
+### Responding to Questions
+
+When exploring categories:
+
+1. Click on any question to add or edit your response
+2. Provide detailed, honest responses about your needs
+3. Set response visibility (public/private)
+4. Navigate back using the **Back button** in the header
+
+---
+
+## Managing Actions
+
+### About Actions
+
+Actions are personal plans you create to help address workplace challenges. Each action is linked to a question from your workplace passport and can be shared with your line manager.
+
+
+
+
+
+
Your personal actions list with Active/Inactive toggles
+
+
+### Working with Actions
+
+You can manage your actions in several ways:
+
+- **Toggle between Active and Inactive status** for each action
+- **Click the link icon** to view the related question
+- **Use 'Show inactive' toggle** to see archived actions
+- **Set actions as public/private** to control sharing
+- **Navigate back to the dashboard** using the Back button in the header
+
+### Sharing Actions
+
+Public actions can be included in emails to your line manager, helping them understand your proactive approach to workplace challenges.
+
+---
+
+## Accessing Resources
+
+### Available Resources
+
+Resources are helpful materials, links, and information related to workplace support and neurodivergent needs. Access them from the Resources section on your dashboard.
+
+
+
+
+
+
Browse available workplace support resources
+
+
+### Using Resources
+
+Resources can help you:
+
+- Learn about workplace accommodations
+- Find support materials for specific challenges
+- Access external links and tools (click the URL to open in a new tab)
+- Discover best practices for workplace inclusion
+- Get guidance on communicating your needs
+- Navigate back to the dashboard using the **Back button** in the header
+
+---
+
+## Sharing Your Workplace Passport
+
+### Email Preview
+
+The email preview shows a summary of your public responses and actions that can be shared with your line manager. Access it by clicking the email button in the header.
+
+
+
+
+
+
Preview your workplace passport before sharing
+
+
+### Before Sending
+
+Review the email content carefully:
+
+- ✓ Check that all information accurately represents your needs
+- ✓ Verify only public responses and actions are included
+- ✓ There's an optional notes field at the bottom for additional context - use this sparingly since the app is designed to track everything through questions, responses, and actions
+- ✓ Remember you control when and what to share
+- ✓ Use the **Back button** in the header to return to the dashboard without sending
+
+### Sending the Email
+
+When you're ready, enter your line manager's email address and click send. The email will be sent with a professional summary of your workplace passport, helping facilitate productive conversations about your needs.
+
+---
+
+## Privacy and Control
+
+### Your Data, Your Choice
+
+Workwise puts you in complete control of your workplace passport:
+
+- **All responses start as private** - nothing is shared unless you explicitly make it public
+- **Change visibility anytime** - toggle between public and private for any response or action
+- **Delete freely** - remove any response or action you no longer want
+- **Preview before sending** - always see exactly what will be shared
+- **No automatic sharing** - emails are only sent when you click send
+
+### Data Storage
+
+Your workplace passport data is securely stored and only accessible to you. When you share your passport via email, only the items marked as "public" are included in the email to your line manager.
+
+### Getting Help
+
+Access context-sensitive help anytime by clicking the **'i' button** in the footer. The help content will adapt based on what you're currently viewing in the application.
+
+---
+
+## Tips for Success
+
+### Getting the Most from Your Workplace Passport
+
+1. **Be honest and specific** - The more detailed your responses, the better your line manager can support you
+2. **Start small** - You don't need to answer everything at once. Focus on the most relevant categories first
+3. **Review regularly** - Your needs may change over time. Update your responses as needed
+4. **Use actions strategically** - Create actions for challenges you're actively working to address
+5. **Control your narrative** - Use the public/private settings to share what feels comfortable
+6. **Preview before sharing** - Always review the email preview to ensure you're comfortable with what's being shared
+
+### Need More Help?
+
+- Click the **'i' button** in the footer for context-specific help
+- Review the **Terms of Use** and **Privacy Policy** in the footer
+- Adjust **font size** in the footer for comfortable reading
+- Use the **Back button** to navigate between views
+
+---
+
+*Generated with Workwise - Your Digital Workplace Passport*
diff --git a/docs/RLS_CONFIG.md b/docs/RLS_CONFIG.md
new file mode 100644
index 0000000..73de272
--- /dev/null
+++ b/docs/RLS_CONFIG.md
@@ -0,0 +1,285 @@
+# Row Level Security (RLS) Configuration
+
+## Overview
+
+This document describes the Row Level Security implementation for the LIFT Digital Workplace Passport application. RLS provides database-level access control to ensure users can only access their own data, protecting sensitive workplace accommodation information.
+
+## Current Implementation Status
+
+### ✅ Development Environment
+- **Status**: Fully implemented and tested
+- **Configuration**: Permissive policies for development workflow
+- **Access**: Anon role can access all data for testing
+
+### ⚠️ Production Environment
+- **Status**: Requires migration deployment
+- **Configuration**: Will enforce strict user-scoped access
+- **Access**: Only authenticated users can access their own data
+
+## Security Architecture
+
+### Database Tables with RLS
+
+All user-related tables have RLS enabled:
+
+- `profiles` - User profile data
+- `responses` - Survey responses
+- `actions` - Follow-up actions
+- `sharing_events` - Email sharing instances
+- `sharing_event_responses` - Shared response snapshots
+- `sharing_event_actions` - Shared action snapshots
+- `line_managers` - Line manager relationships
+
+### Public Tables (No RLS)
+
+These tables contain non-sensitive data accessible to all users:
+
+- `questions` - Survey questions
+- `organizations` - Organizational data
+- `resources` - Public resources
+
+## Policy Structure
+
+### Development Policies (Current)
+
+**Purpose**: Enable development workflow without authentication barriers
+
+```sql
+-- Example: Profiles table policy
+CREATE POLICY "Allow profile access" ON profiles
+ FOR ALL USING (
+ auth.role() = 'anon' OR -- Development access
+ auth.role() = 'service_role' OR -- Admin access
+ auth.uid() = user_id -- User's own data
+ );
+```
+
+**Access Levels**:
+- `anon` role: Full access (development only)
+- `service_role`: Full access (admin operations)
+- `authenticated` users: Own data only
+
+### Production Policies (Target)
+
+**Purpose**: Enforce strict data isolation for production security
+
+```sql
+-- Example: Production-ready policy
+CREATE POLICY "Users can access own profiles" ON profiles
+ FOR ALL USING (
+ auth.role() = 'service_role' OR -- Admin access only
+ auth.uid() = user_id -- User's own data
+ );
+```
+
+**Access Levels**:
+- `service_role`: Full access (admin operations only)
+- `authenticated` users: Own data only
+- `anon` role: **NO ACCESS** (security requirement)
+
+## Migration Files
+
+### Applied Migrations
+
+1. **`20250923195428_enable_row_level_security.sql`**
+ - Enables RLS on all user tables
+ - Creates initial strict policies
+ - **Status**: Applied to development
+
+2. **`20250923205100_allow_service_role_access.sql`**
+ - Adds service role bypass for admin operations
+ - **Status**: Applied to development
+
+3. **`20250923205334_allow_anon_access_for_dev.sql`**
+ - Adds anon role access for development
+ - **Status**: Applied to development, **DO NOT APPLY TO PRODUCTION**
+
+### Production Migration Strategy
+
+**Option A: Apply all migrations then remove dev access**
+```bash
+# Apply all existing migrations
+npx supabase db push
+
+# Create and apply production hardening migration
+npx supabase migration new harden_rls_for_production
+# This migration removes anon access
+```
+
+**Option B: Apply only production-ready migrations**
+```bash
+# Apply only the first two migrations to production
+# Skip the dev-specific migration
+```
+
+## Production Deployment Steps
+
+### Prerequisites
+
+1. **Magic Link Authentication**: Must be implemented before production deployment
+2. **User Registration Flow**: Profile creation on first login
+3. **Session Management**: Proper JWT token handling
+
+### Step 1: Apply RLS Migrations
+
+```bash
+# Navigate to project directory
+cd /path/to/project
+
+# Apply migrations to production database
+npx supabase db push --linked
+
+# Or using production script
+./scripts/prod-run-migrations.sh
+```
+
+### Step 2: Create Production Hardening Migration
+
+Create `supabase/migrations/YYYYMMDDHHMMSS_harden_rls_for_production.sql`:
+
+```sql
+-- Remove anon access for production security
+-- Keep only authenticated user access and service role for admin
+
+-- Update all table policies to remove anon role
+DROP POLICY IF EXISTS "Allow profile access" ON profiles;
+CREATE POLICY "Users can access own profiles" ON profiles
+ FOR ALL USING (
+ auth.role() = 'service_role' OR
+ auth.uid() = user_id
+ );
+
+-- Repeat for all other tables...
+```
+
+### Step 3: Verify Security
+
+**Test authenticated access**:
+```sql
+-- As authenticated user, should only see own data
+SELECT COUNT(*) FROM profiles; -- Should return 1 (own profile)
+```
+
+**Test anon access**:
+```sql
+-- As anon user, should see no data
+SELECT COUNT(*) FROM profiles; -- Should return 0
+```
+
+## Security Considerations
+
+### Current Vulnerabilities (Development Only)
+
+⚠️ **CRITICAL**: Development environment allows anon access to all user data
+- **Risk**: Complete data exposure without authentication
+- **Mitigation**: DO NOT deploy dev migrations to production
+- **Timeline**: Must be resolved before production launch
+
+### Production Security Features
+
+✅ **Database-Level Protection**: RLS enforced at PostgreSQL level
+✅ **Defense in Depth**: Application + database security layers
+✅ **Audit Trail**: All access attempts logged by Supabase
+✅ **User Isolation**: Zero data leakage between users
+
+### Authentication Integration
+
+**Required for Production**:
+1. Magic link authentication implementation
+2. JWT token validation in RLS policies
+3. User profile creation on first login
+4. Session management and refresh
+
+**RLS Policy Dependencies**:
+- `auth.uid()` requires valid JWT token
+- `auth.role()` identifies user permissions
+- Profile relationships enable data scoping
+
+## Testing & Verification
+
+### Development Testing
+
+```bash
+# Verify anon access works (development)
+curl -H "Authorization: Bearer $ANON_KEY" \
+ "$SUPABASE_URL/rest/v1/profiles"
+
+# Should return all profiles
+```
+
+### Production Testing
+
+```bash
+# Verify anon access blocked (production)
+curl -H "Authorization: Bearer $ANON_KEY" \
+ "$SUPABASE_URL/rest/v1/profiles"
+
+# Should return empty array or 401 error
+
+# Verify authenticated access (production)
+curl -H "Authorization: Bearer $USER_JWT" \
+ "$SUPABASE_URL/rest/v1/profiles"
+
+# Should return only user's own profile
+```
+
+## Troubleshooting
+
+### Common Issues
+
+**Problem**: App can't load data after RLS deployment
+- **Cause**: Anon key blocked by production policies
+- **Solution**: Implement authentication before RLS deployment
+
+**Problem**: Users see other users' data
+- **Cause**: RLS policies not properly scoped
+- **Solution**: Verify `auth.uid() = user_id` in all policies
+
+**Problem**: Admin operations fail
+- **Cause**: Missing service role bypass
+- **Solution**: Ensure `auth.role() = 'service_role'` in policies
+
+### Debugging Commands
+
+```sql
+-- Check RLS status
+SELECT schemaname, tablename, rowsecurity
+FROM pg_tables
+WHERE schemaname = 'public';
+
+-- View current policies
+SELECT * FROM pg_policies
+WHERE tablename = 'profiles';
+
+-- Test policy as different roles
+SET ROLE anon;
+SELECT COUNT(*) FROM profiles;
+
+SET ROLE authenticated;
+SELECT COUNT(*) FROM profiles;
+```
+
+## Migration Rollback Plan
+
+If RLS causes production issues:
+
+```sql
+-- Emergency disable RLS (temporary)
+ALTER TABLE profiles DISABLE ROW LEVEL SECURITY;
+-- Repeat for other tables...
+
+-- Re-enable after fixing policies
+ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
+```
+
+## Next Steps
+
+1. **Implement Magic Link Authentication**
+2. **Create Production Hardening Migration**
+3. **Test Authentication Flow with RLS**
+4. **Deploy to Production with Monitoring**
+5. **Verify Security in Production Environment**
+
+---
+
+**⚠️ IMPORTANT**: The current development configuration is NOT production-ready. The anon role bypass must be removed before production deployment to prevent unauthorized data access.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index cdd6b25..1d934d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,20 +1,20 @@
{
- "name": "Workwise",
- "version": "0.6.005",
+ "name": "workwise",
+ "version": "0.6.008",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "Workwise",
- "version": "0.6.005",
+ "name": "workwise",
+ "version": "0.6.008",
"dependencies": {
"@fontsource/metropolis": "^5.2.5",
"@supabase/supabase-js": "^2.49.8",
"@tailwindcss/vite": "^4.1.10",
"daisyui": "^5.0.43",
"dotenv": "^16.5.0",
+ "isomorphic-dompurify": "^2.28.0",
"tailwindcss": "^4.1.10",
- "vite": "^5.0.3",
"zod": "^3.25.51"
},
"devDependencies": {
@@ -37,7 +37,7 @@
"prettier-plugin-sql": "^0.19.1",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.12",
- "supabase": "^2.24.3",
+ "supabase": "^2.40.7",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"svelte-hero-icons": "^5.2.0",
@@ -81,6 +81,31 @@
"lru-cache": "^10.4.3"
}
},
+ "node_modules/@asamuzakjp/dom-selector": {
+ "version": "6.5.6",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.5.6.tgz",
+ "integrity": "sha512-Mj3Hu9ymlsERd7WOsUKNUZnJYL4IZ/I9wVVYgtvOsWYiEFbkQ4G7VRIh2USxTVW4BBDIsLG+gBUgqOqf2Kvqow==",
+ "dependencies": {
+ "@asamuzakjp/nwsapi": "^2.3.9",
+ "bidi-js": "^1.0.3",
+ "css-tree": "^3.1.0",
+ "is-potential-custom-element-name": "^1.0.1",
+ "lru-cache": "^11.2.1"
+ }
+ },
+ "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
+ "version": "11.2.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz",
+ "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@asamuzakjp/nwsapi": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
+ "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="
+ },
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -117,10 +142,9 @@
}
},
"node_modules/@csstools/color-helpers": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz",
- "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==",
- "dev": true,
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+ "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
"funding": [
{
"type": "github",
@@ -131,7 +155,6 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT-0",
"engines": {
"node": ">=18"
}
@@ -140,7 +163,6 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
"integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -161,10 +183,9 @@
}
},
"node_modules/@csstools/css-color-parser": {
- "version": "3.0.10",
- "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz",
- "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==",
- "dev": true,
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+ "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
"funding": [
{
"type": "github",
@@ -175,9 +196,8 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"dependencies": {
- "@csstools/color-helpers": "^5.0.2",
+ "@csstools/color-helpers": "^5.1.0",
"@csstools/css-calc": "^2.1.4"
},
"engines": {
@@ -192,7 +212,6 @@
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
"integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -211,11 +230,31 @@
"@csstools/css-tokenizer": "^3.0.4"
}
},
+ "node_modules/@csstools/css-syntax-patches-for-csstree": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.14.tgz",
+ "integrity": "sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
"integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -1953,6 +1992,12 @@
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==",
"license": "MIT"
},
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "optional": true
+ },
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
@@ -2418,7 +2463,6 @@
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
@@ -2556,6 +2600,14 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/bidi-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
+ "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
+ "dependencies": {
+ "require-from-string": "^2.0.2"
+ }
+ },
"node_modules/big-integer": {
"version": "1.6.52",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
@@ -2846,6 +2898,18 @@
"node": ">= 8"
}
},
+ "node_modules/css-tree": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
+ "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+ "dependencies": {
+ "mdn-data": "2.12.2",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
"node_modules/css.escape": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
@@ -2944,7 +3008,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -2962,7 +3025,6 @@
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
"integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
- "dev": true,
"license": "MIT"
},
"node_modules/deep-eql": {
@@ -3032,6 +3094,14 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/dompurify": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
+ "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/dotenv": {
"version": "16.5.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
@@ -3082,7 +3152,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -3745,7 +3814,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"whatwg-encoding": "^3.1.1"
@@ -3758,7 +3826,6 @@
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
@@ -3772,7 +3839,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
@@ -3786,7 +3852,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -3899,7 +3964,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/is-reference": {
@@ -3919,6 +3983,159 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/isomorphic-dompurify": {
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.28.0.tgz",
+ "integrity": "sha512-9G5v8g4tYoix5odskjG704Khm1zNrqqqOC4YjCwEUhx0OvuaijRCprAV2GwJ9iw/01c6H1R+rs/2AXPZLlgDaQ==",
+ "dependencies": {
+ "dompurify": "^3.2.7",
+ "jsdom": "^27.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/@asamuzakjp/css-color": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz",
+ "integrity": "sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.4",
+ "@csstools/css-color-parser": "^3.1.0",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "lru-cache": "^11.2.1"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/cssstyle": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.1.tgz",
+ "integrity": "sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==",
+ "dependencies": {
+ "@asamuzakjp/css-color": "^4.0.3",
+ "@csstools/css-syntax-patches-for-csstree": "^1.0.14",
+ "css-tree": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/data-urls": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz",
+ "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^15.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/jsdom": {
+ "version": "27.0.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.0.0.tgz",
+ "integrity": "sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A==",
+ "dependencies": {
+ "@asamuzakjp/dom-selector": "^6.5.4",
+ "cssstyle": "^5.3.0",
+ "data-urls": "^6.0.0",
+ "decimal.js": "^10.5.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "is-potential-custom-element-name": "^1.0.1",
+ "parse5": "^7.3.0",
+ "rrweb-cssom": "^0.8.0",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^6.0.0",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^8.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^15.0.0",
+ "ws": "^8.18.2",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "canvas": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/lru-cache": {
+ "version": "11.2.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz",
+ "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/tldts": {
+ "version": "7.0.16",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.16.tgz",
+ "integrity": "sha512-5bdPHSwbKTeHmXrgecID4Ljff8rQjv7g8zKQPkCozRo2HWWni+p310FSn5ImI+9kWw9kK4lzOB5q/a6iv0IJsw==",
+ "dependencies": {
+ "tldts-core": "^7.0.16"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/tldts-core": {
+ "version": "7.0.16",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.16.tgz",
+ "integrity": "sha512-XHhPmHxphLi+LGbH0G/O7dmUH9V65OY20R7vH8gETHsp5AZCjBk9l8sqmRKLaGOxnETU7XNSDUPtewAy/K6jbA=="
+ },
+ "node_modules/isomorphic-dompurify/node_modules/tough-cookie": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
+ "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
+ "dependencies": {
+ "tldts": "^7.0.5"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/tr46": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
+ "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/webidl-conversions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz",
+ "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/isomorphic-dompurify/node_modules/whatwg-url": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz",
+ "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==",
+ "dependencies": {
+ "tr46": "^6.0.0",
+ "webidl-conversions": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
@@ -4425,6 +4642,11 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
+ "node_modules/mdn-data": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
+ "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4495,10 +4717,9 @@
}
},
"node_modules/minizlib": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
- "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
- "license": "MIT",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
"dependencies": {
"minipass": "^7.1.2"
},
@@ -4506,21 +4727,6 @@
"node": ">= 18"
}
},
- "node_modules/mkdirp": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
- "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
- "license": "MIT",
- "bin": {
- "mkdirp": "dist/cjs/src/bin.js"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
@@ -4552,7 +4758,6 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
@@ -4795,7 +5000,6 @@
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
@@ -5215,7 +5419,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -5308,6 +5511,14 @@
"node": ">=8"
}
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -5388,7 +5599,6 @@
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
"integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
- "dev": true,
"license": "MIT"
},
"node_modules/run-parallel": {
@@ -5432,14 +5642,12 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true,
"license": "MIT"
},
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
- "dev": true,
"license": "ISC",
"dependencies": {
"xmlchars": "^2.2.0"
@@ -5707,17 +5915,16 @@
"license": "MIT"
},
"node_modules/supabase": {
- "version": "2.30.4",
- "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.30.4.tgz",
- "integrity": "sha512-AOCyd2vmBBMTXbnahiCU0reRNxKS4n5CrPciUF2tcTrQ8dLzl1HwcLfe5DrG8E0QRcKHPDdzprmh/2+y4Ta5MA==",
+ "version": "2.45.5",
+ "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.45.5.tgz",
+ "integrity": "sha512-/toOX6bHYx2TUNA5AtlzrKKfvctbLQ8R6QqvUCAT20KtZOkR14HpBYav3TNDaU5owPeB58cT5Uftvxw36Tb95A==",
"dev": true,
"hasInstallScript": true,
- "license": "MIT",
"dependencies": {
"bin-links": "^5.0.0",
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2",
- "tar": "7.4.3"
+ "tar": "7.4.4"
},
"bin": {
"supabase": "bin/supabase"
@@ -5870,7 +6077,6 @@
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
- "dev": true,
"license": "MIT"
},
"node_modules/tailwindcss": {
@@ -5889,16 +6095,14 @@
}
},
"node_modules/tar": {
- "version": "7.4.3",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
- "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
- "license": "ISC",
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.4.tgz",
+ "integrity": "sha512-O1z7ajPkjTgEgmTGz0v9X4eqeEXTDREPTO77pVC1Nbs86feBU1Zhdg+edzavPmYW1olxkwsqA2v4uOw6E8LeDg==",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
"minipass": "^7.1.2",
- "minizlib": "^3.0.1",
- "mkdirp": "^3.0.1",
+ "minizlib": "^3.1.0",
"yallist": "^5.0.0"
},
"engines": {
@@ -6344,7 +6548,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"xml-name-validator": "^5.0.0"
@@ -6377,7 +6580,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
@@ -6390,7 +6592,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -6579,7 +6780,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
- "dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18"
@@ -6589,7 +6789,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
- "dev": true,
"license": "MIT"
},
"node_modules/yallist": {
@@ -6601,20 +6800,6 @@
"node": ">=18"
}
},
- "node_modules/yaml": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
- "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- }
- },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index b0ea10c..623f7d2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
- "name": "Workwise",
+ "name": "workwise",
"private": true,
- "version": "0.6.008",
+ "version": "0.6.026",
"type": "module",
"scripts": {
"dev": "vite dev",
@@ -40,7 +40,7 @@
"prettier-plugin-sql": "^0.19.1",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.12",
- "supabase": "^2.24.3",
+ "supabase": "^2.40.7",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"svelte-hero-icons": "^5.2.0",
@@ -55,8 +55,8 @@
"@tailwindcss/vite": "^4.1.10",
"daisyui": "^5.0.43",
"dotenv": "^16.5.0",
+ "isomorphic-dompurify": "^2.28.0",
"tailwindcss": "^4.1.10",
- "vite": "^5.0.3",
"zod": "^3.25.51"
}
}
diff --git a/scripts/generate-test-data.sh b/scripts/generate-test-data.sh
index 4f72a67..aa88c76 100755
--- a/scripts/generate-test-data.sh
+++ b/scripts/generate-test-data.sh
@@ -173,16 +173,15 @@ echo "-- ===========================================" >> supabase/generated/test
echo "" >> supabase/generated/test_fake_data.sql
# Add responses with dynamic question lookup
-echo "$TEST_DATA" | jq -r '.responses[] |
-"INSERT INTO responses (id, user_id, question_id, response_text, status, visibility, version)
-SELECT
+echo "$TEST_DATA" | jq -r '.responses[] |
+"INSERT INTO responses (id, user_id, question_id, response_text, status, visibility)
+SELECT
\u0027" + .id + "\u0027::uuid,
\u0027" + .user_id + "\u0027::uuid,
q.id,
\u0027" + (.response_text | gsub("\u0027"; "\u0027\u0027")) + "\u0027,
\u0027" + .status + "\u0027,
- \u0027" + .visibility + "\u0027,
- " + (.version | tostring) + "
+ \u0027" + .visibility + "\u0027
FROM questions q WHERE q.category = \u0027" + .question_category + "\u0027 AND q.\"order\" = " + (.question_order | tostring) + ";"' >> supabase/generated/test_fake_data.sql
echo "" >> supabase/generated/test_fake_data.sql
@@ -192,9 +191,9 @@ echo "-- ===========================================" >> supabase/generated/test
echo "" >> supabase/generated/test_fake_data.sql
# Add actions
-echo "$TEST_DATA" | jq -r '.actions[] |
-"INSERT INTO actions (id, user_id, response_id, type, description, status, version) VALUES
- (\u0027" + .id + "\u0027::uuid, \u0027" + .user_id + "\u0027::uuid, \u0027" + .response_id + "\u0027::uuid, \u0027" + .type + "\u0027, \u0027" + (.description | gsub("\u0027"; "\u0027\u0027")) + "\u0027, \u0027" + .status + "\u0027, " + (.version | tostring) + ");"' >> supabase/generated/test_fake_data.sql
+echo "$TEST_DATA" | jq -r '.actions[] |
+"INSERT INTO actions (id, user_id, response_id, type, description, status) VALUES
+ (\u0027" + .id + "\u0027::uuid, \u0027" + .user_id + "\u0027::uuid, \u0027" + .response_id + "\u0027::uuid, \u0027" + .type + "\u0027, \u0027" + (.description | gsub("\u0027"; "\u0027\u0027")) + "\u0027, \u0027" + .status + "\u0027);"' >> supabase/generated/test_fake_data.sql
echo "" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
@@ -340,9 +339,7 @@ echo "$TEST_DATA" | jq -r '.organizations | map(" \u0027" + .id + "\u0027::uu
cat >> supabase/generated/delete_test_fake_data.sql << 'EOF'
);
--- Reset sequences to ensure clean state
-SELECT setval('response_version_seq', 1, false);
-SELECT setval('action_version_seq', 1, false);
+-- No sequences to reset after removing versioning system
-- Verification queries (uncomment to see results)
/*
diff --git a/scripts/prod-seed-test-data.sh b/scripts/prod-seed-test-data.sh
index eefb8ad..a9a691a 100755
--- a/scripts/prod-seed-test-data.sh
+++ b/scripts/prod-seed-test-data.sh
@@ -176,13 +176,12 @@ echo "$TEST_DATA" | jq -c '.responses[]' | while read -r response; do
RESPONSE_TEXT=$(echo "$response" | jq -r '.response_text')
STATUS=$(echo "$response" | jq -r '.status')
VISIBILITY=$(echo "$response" | jq -r '.visibility')
- VERSION=$(echo "$response" | jq -r '.version')
-
+
# Get question ID (note: "order" is a reserved word, so we need to escape it)
QUESTION_ID=$(curl -s -X GET "$API_URL/questions?category=eq.$QUESTION_CATEGORY&%22order%22=eq.$QUESTION_ORDER&select=id" \
-H "$AUTH_HEADER" \
-H "$APIKEY_HEADER" 2>/dev/null | jq -r '.[0].id // empty')
-
+
if [ ! -z "$QUESTION_ID" ]; then
curl -s -X POST "$API_URL/responses" \
-H "$AUTH_HEADER" \
@@ -195,8 +194,7 @@ echo "$TEST_DATA" | jq -c '.responses[]' | while read -r response; do
\"question_id\": \"$QUESTION_ID\",
\"response_text\": \"$RESPONSE_TEXT\",
\"status\": \"$STATUS\",
- \"visibility\": \"$VISIBILITY\",
- \"version\": $VERSION
+ \"visibility\": \"$VISIBILITY\"
}" > /dev/null
echo "✅ Created response for question $QUESTION_ORDER in $QUESTION_CATEGORY"
else
diff --git a/src/app.css b/src/app.css
index 8f0abda..b3aa431 100644
--- a/src/app.css
+++ b/src/app.css
@@ -56,65 +56,154 @@ h3 {
@apply mb-2 text-center text-lg;
}
+/* Override prose headings in modals to be visible */
+.prose h1,
+.prose h2,
+.prose h3,
+.prose h4,
+.prose h5,
+.prose h6 {
+ color: var(--color-base-content);
+}
+
+.prose p,
+.prose li,
+.prose ul,
+.prose ol {
+ color: var(--color-base-content);
+}
+
.dash-grid-1 {
@apply grid grid-cols-1 justify-items-center gap-4 p-4;
}
.dash-grid-2 {
- @apply mt-2 grid grid-cols-2;
+ @apply mt-2 grid grid-cols-2 gap-2;
}
.dash-tile-rect {
- @apply card border-primary bg-base-100 m-2 rounded-lg border p-3 transition-shadow hover:shadow-md;
+ @apply card border-primary bg-base-100 m-2 rounded-lg border p-3 shadow-sm transition-all;
@apply cursor-pointer;
}
+.dash-tile-rect:disabled {
+ @apply cursor-not-allowed opacity-60;
+}
+
.dash-tile-square {
- @apply card border-primary bg-base-100 mx-10 my-1 rounded-lg border p-3 transition-shadow hover:shadow-md;
+ @apply card border-primary bg-base-100 mx-2 my-1 rounded-lg border p-3 shadow-sm transition-all;
@apply flex flex-col items-center justify-center text-center;
- @apply cursor-pointer;
- /* Height constraints relative to .list-item */
- /* .list-item has m-2 (0.5rem top+bottom) + p-3 (0.75rem top+bottom) + content = base height */
- /* Base height ≈ 1rem (margin) + 1.5rem (padding) + 1.5rem (content) = 4rem minimum */
- /* min-height: 8rem; /* 2x base height */
- /* max-height: 10rem; 5x base height */
- /* min-height: 3rem; /* 2x base height */
- /* max-height: 4rem; 5x base height */
+ @apply min-w-0 cursor-pointer;
+}
+
+.dash-tile-square:hover:not(:disabled) {
+ @apply border-secondary shadow-md;
+ transform: translateY(-1px);
+}
+
+.dash-tile-square:active:not(:disabled) {
+ @apply shadow-sm;
+ transform: translateY(0);
+}
+
+.dash-tile-square:disabled {
+ @apply cursor-not-allowed opacity-60;
}
.dash-vertical-container {
- @apply flex flex-col space-y-4 px-4;
+ @apply flex flex-col;
+}
+
+/* Remove gap from card-body */
+.card-body {
+ gap: 0;
+}
+
+/* Mobile card title spacing */
+@media (max-width: 480px) {
+ .card-title {
+ @apply mb-0;
+ }
}
.footer {
- @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);
+ @apply bg-primary text-primary-content flex h-16 items-center justify-between px-6;
+ container-type: inline-size;
+}
+
+/* Reduce footer padding and height on small screens */
+@media (max-width: 480px) {
+ .footer {
+ @apply h-12 px-3;
+ }
+}
+
+/* Container queries for footer text hiding */
+@container (max-width: 500px) {
+ .footer-text {
+ display: none;
+ }
+}
+
+@container (min-width: 501px) {
+ .footer-text {
+ display: inline;
+ }
}
.footer-content {
@apply flex w-full flex-row flex-nowrap justify-between align-middle;
}
+.footer-text {
+ @apply text-base font-medium;
+}
+
.header {
@apply bg-primary sticky top-0 z-50 h-20 w-full flex-shrink-0;
+ @apply flex items-center justify-between px-5;
}
-.header-container-name {
- @apply w-auto;
+.header-left {
+ @apply flex items-center space-x-4;
}
-.header-container-logo {
+.header-right {
+ @apply flex items-center space-x-3;
+}
+
+.logo-button {
@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 px-5;
+/* Hide logo on very small screens to prevent overlap */
+@media (max-width: 250px) {
+ .logo-button {
+ display: none;
+ }
}
-.header-left {
- @apply flex items-center space-x-4;
+/* Ensure no overlap on small screens */
+@media (max-width: 480px) {
+ .header {
+ @apply h-16 px-3;
+ }
+
+ .header-left {
+ @apply min-w-0 flex-1 space-x-2;
+ }
+
+ .header-left h1 {
+ @apply truncate text-xl;
+ }
+
+ .header-right {
+ @apply flex-shrink-0 space-x-2;
+ }
+
+ .logo-button {
+ @apply p-1;
+ }
}
.view {
@@ -122,11 +211,25 @@ h3 {
}
.view-header {
- @apply bg-accent h-14 max-w-full px-5;
- /* height: 4.5rem; */
+ @apply bg-accent max-w-full px-5;
+ min-height: 44px;
display: grid;
grid-template-columns: 1fr auto;
- align-items: center;
+ align-items: start;
+ gap: 1rem;
+ padding-top: 1rem;
+ padding-bottom: 0.75rem;
+}
+
+/* Make view-header more compact on mobile */
+@media (max-width: 480px) {
+ .view-header {
+ @apply px-3;
+ min-height: 44px;
+ gap: 0.5rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ }
}
.view-layout {
@@ -136,7 +239,7 @@ h3 {
.view-content {
/* LIST */
- @apply list /* justify-left */ m-2 mx-auto flex w-full max-w-5xl flex-1 flex-col overflow-y-auto px-2;
+ @apply list mx-auto flex w-full max-w-3xl flex-1 flex-col overflow-y-auto px-2 py-2;
}
.card-container {
@@ -147,6 +250,13 @@ h3 {
@apply card bg-base-100 mt-6 mb-4 max-w-full p-2 shadow;
}
+/* Reduce top margin on mobile */
+@media (max-width: 480px) {
+ .card-header {
+ @apply mt-2;
+ }
+}
+
.card-content {
@apply card bg-base-100 max-w-full p-2 shadow;
}
@@ -160,7 +270,7 @@ h3 {
}
.form-fieldset {
- @apply border-primary flex justify-between rounded-xl border-2 p-4;
+ @apply border-primary flex items-start justify-between rounded-xl border-2 p-4;
}
.status-indicator {
@@ -204,7 +314,13 @@ h3 {
}
.btn-nav {
- @apply btn btn-secondary text-secondary-content;
+ @apply btn btn-sm btn-ghost text-accent-content border-2 border-white text-base;
+ height: 1.75rem;
+ min-height: 1.75rem;
+}
+
+.btn-nav:hover {
+ @apply bg-white/20;
}
.btn-nav:disabled {
@@ -220,17 +336,57 @@ h3 {
}
.list-item {
- @apply card border-primary bg-base-100 m-2 rounded-lg border p-3 transition-shadow hover:shadow-md;
+ @apply card border-primary bg-base-100 m-2 cursor-default rounded-lg border p-3 shadow-sm;
+}
+
+.list-item-questions {
+ @apply cursor-pointer transition-all;
+}
+
+.list-item-questions:hover {
+ @apply border-secondary shadow-md;
+}
+
+.list-item-actions {
+ /* No hover effects */
+}
+
+.list-item-resources {
+ /* No hover effects */
}
.list-item-row {
@apply flex w-full flex-row items-center justify-between;
}
+.list-item-row-questions {
+ @apply flex w-full flex-row items-center justify-between;
+}
+
+.list-item-row-resources {
+ @apply flex w-full flex-row items-center justify-center;
+}
+
+.list-item-row-actions {
+ @apply flex w-full flex-col items-start md:flex-row md:items-center md:justify-between;
+}
+
.list-item-content {
@apply min-w-0 flex-1 px-3;
}
+.list-item-content-actions {
+ @apply max-w-none min-w-0 flex-1 px-3;
+}
+
+.resource-link {
+ @apply text-accent text-sm break-all underline;
+}
+
+.resource-link:hover {
+ color: var(--color-accent-dark);
+}
+
.list-item-wrapper {
@apply flex w-full;
}
@@ -264,20 +420,34 @@ h3 {
}
.breadcrumb-list {
- @apply m-0 flex list-none items-center p-0;
- white-space: nowrap;
- overflow: hidden;
+ @apply m-0 flex list-none items-start p-0;
min-width: 0;
flex: 1;
+ flex-wrap: wrap;
+ gap: 0 0.5rem;
+ align-content: flex-start;
+ row-gap: 0.25rem;
}
.breadcrumb-item {
- @apply flex flex-shrink-0 items-center;
+ @apply flex items-start;
+ flex-shrink: 0;
+ /* Allow wrapping but keep items together with their separators */
+}
+
+/* Continuation indicator for wrapped breadcrumb lines */
+.breadcrumb-continuation {
+ @apply text-accent-content/50;
+ margin-right: 0.25rem;
+ font-size: 1rem;
+ line-height: 1;
+ user-select: none;
}
.breadcrumb-item:last-child {
- @apply min-w-0 flex-shrink;
- max-width: 100%;
+ @apply min-w-0;
+ flex-shrink: 1;
+ /* Last item can shrink and truncate if really needed */
}
.breadcrumb-link {
@@ -293,7 +463,40 @@ h3 {
.breadcrumb-separator {
@apply text-accent-content/70 flex-shrink-0 font-normal;
- margin: 0 0.25rem;
+ margin: 0 0.125rem;
+}
+
+/* Mobile optimizations */
+@media (max-width: 640px) {
+ .breadcrumb {
+ font-size: 1rem;
+ }
+
+ .breadcrumb-list {
+ line-height: 1.4;
+ /* Better line spacing for wrapped breadcrumbs */
+ }
+
+ .breadcrumb-link {
+ @apply text-sm;
+ }
+
+ .breadcrumb-current {
+ @apply text-sm;
+ }
+
+ .breadcrumb-separator {
+ margin: 0 0.0625rem;
+ }
+}
+
+/* Very small screens: More aggressive wrapping */
+@media (max-width: 480px) {
+ .breadcrumb-item {
+ /* Allow more aggressive wrapping on very small screens */
+ flex-shrink: 1;
+ min-width: fit-content;
+ }
}
.actions-crud {
@@ -313,15 +516,23 @@ h3 {
}
.action-view {
- @apply flex items-start justify-between gap-3;
+ @apply flex flex-col gap-2;
}
.action-content {
- @apply flex-1;
+ /* Not needed - remove this wrapper */
}
.action-description {
- @apply text-base-content mb-2 font-medium;
+ @apply text-base-content font-medium;
+ /* Give description its own full-width row */
+ word-break: break-word;
+ overflow-wrap: break-word;
+}
+
+.action-meta {
+ @apply flex flex-wrap items-center justify-between gap-2;
+ /* Date on left, buttons on right, can wrap if needed */
}
.action-date {
@@ -329,7 +540,18 @@ h3 {
}
.action-buttons {
- @apply flex flex-shrink-0 gap-2;
+ @apply flex gap-2;
+}
+
+/* Small screens: Force buttons to wrap below date */
+@media (max-width: 480px) {
+ .action-meta {
+ @apply flex-col items-start gap-2;
+ }
+
+ .action-buttons {
+ @apply self-end;
+ }
}
.action-edit-form {
@@ -409,11 +631,33 @@ h3 {
}
@media (max-width: 375px) {
- .dash-grid {
- @apply grid-cols-1;
+ .dash-grid-2 {
+ @apply grid-cols-1 gap-4;
}
.dash-tile-square {
- @apply my-1 aspect-auto min-h-[100px];
+ @apply mx-4 my-1 aspect-auto min-h-[100px];
+ }
+}
+
+/* Modal styles */
+.modal-box-custom {
+ @apply m-4 mx-auto;
+ max-height: calc(100vh - 2rem);
+ height: auto;
+}
+
+.modal-box-help {
+ @apply m-4 max-w-4xl mx-auto;
+ max-height: calc(100vh - 2rem);
+ height: auto;
+}
+
+/* Ensure modals fit on mobile */
+@media (max-height: 600px) {
+ .modal-box-custom,
+ .modal-box-help {
+ @apply m-2;
+ max-height: calc(100vh - 1rem);
}
}
diff --git a/src/lib/components/cards/DashTile.svelte b/src/lib/components/cards/DashTile.svelte
index 9896aec..24ebed4 100644
--- a/src/lib/components/cards/DashTile.svelte
+++ b/src/lib/components/cards/DashTile.svelte
@@ -1,17 +1,6 @@
diff --git a/src/lib/components/cards/ListItem.svelte b/src/lib/components/cards/ListItem.svelte
index a003f08..e7d7220 100644
--- a/src/lib/components/cards/ListItem.svelte
+++ b/src/lib/components/cards/ListItem.svelte
@@ -1,17 +1,17 @@
{:then data}
{#if data.question && data.question.data}
-
- Welcome to Workwise ("Service"), a workplace passport application provided by LIFT. By using our
- Service, you agree to these Terms of Use. Please read them carefully.
-
-
- The Service allows you to create, maintain, and selectively share workplace statements about
- your preferences, needs, and working style with your line managers and employers.
-
-
-
2. User Accounts
-
- Authentication is provided through a secure magic link system sent to your email address. You
- are responsible for:
-
-
-
Maintaining the security of your email account
-
Only using magic links sent directly to your email
-
Notifying us of any unauthorized access
-
-
- If you register with a new email address, a new passport will be created that cannot be merged
- with existing data. This is intentional for data protection reasons.
-
-
-
3. Your Content
-
You retain ownership of all content you create through our Service. By using Workwise, you:
-
-
Are responsible for the accuracy of your statements
-
Control which statements are marked as "public" and can be shared
-
Decide when to share information with your line manager
-
Can update or delete your content at any time
-
-
-
4. Acceptable Use
-
You agree not to use Workwise to:
-
-
Create or share statements that are harmful, offensive, or discriminatory
-
Attempt to gain unauthorized access to other users' data
-
Upload malicious code or attempt to compromise the service
-
Impersonate others or provide false information
-
-
-
5. Legal Terms
-
- Service Modifications: We may modify or discontinue the Service at any time with
- reasonable notice.
-
-
- Limitation of Liability: To the fullest extent permitted by law, LIFT shall not
- be liable for any indirect, incidental, special, consequential, or punitive damages.
-
-
- Governing Law: These Terms are governed by the laws of [Jurisdiction], without regard
- to its conflict of law principles.
-
-
-
- (showPrivacyModal = false)}
->
-
How We Protect Your Data
-
- At Workwise, we prioritize your privacy and ensure your personal information is handled
- securely.
-
-
-
Your data is encrypted both in transit and at rest
-
We use secure passwordless authentication (Magic Links) to protect your account
-
We only collect the minimum information needed to provide our service
-
You retain complete control over your data at all times
-
-
-
Your Rights
-
Under data protection law, you have the right to:
-
-
Access your personal data
-
Correct inaccurate data
-
Request deletion of your data
-
Restrict or object to processing of your data
-
Obtain and reuse your data (data portability)
-
Withdraw consent at any time
-
-
-
Data Sharing
-
We only share your data:
-
-
With your explicit consent
-
Only statements marked as "public" can be shared with your line manager
-
You control when and how your data is shared
-
Email sharing is initiated by you and only sent to the email address you provide
-
-
diff --git a/src/lib/components/layouts/Header.svelte b/src/lib/components/layouts/Header.svelte
index 93bffc9..3bc4e71 100644
--- a/src/lib/components/layouts/Header.svelte
+++ b/src/lib/components/layouts/Header.svelte
@@ -2,7 +2,6 @@
import { getContext } from 'svelte';
import type { AppState, ViewName } from '$lib/types/appState';
import Tooltip from '../ui/Tooltip.svelte';
- import FontSizeControl from '../ui/FontSizeControl.svelte';
import { Icon, Envelope } from 'svelte-hero-icons';
import { version } from '$lib/version';
@@ -19,37 +18,105 @@
};
const onEmailClick = () => {
setViewName('email');
+ // Move focus to back button after navigation
+ setTimeout(() => {
+ const backButton = document.querySelector('[aria-label*="Back"]');
+ backButton?.focus();
+ }, 0);
};
-
-
-