Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,29 @@ Before submitting any code, ensure the following steps are completed:

## Deployment (Vercel)

### IMPORTANT: Production Deployment Rules

⚠️ **CRITICAL**: Production (`https://lift02.vercel.app`) is MANUALLY controlled. DO NOT deploy to production accidentally.

**Deployment Commands:**

```bash
# PREVIEW deployment (safe - for testing)
npm run deploy
# or
npx vercel

# PRODUCTION deployment (MANUAL ONLY - requires explicit approval)
npx vercel --prod
```

**Safe Workflow:**
1. Work on feature branches (e.g., `feat/implement_auth`)
2. Use `npm run deploy` to create preview deployments for testing
3. Preview deployments get unique URLs like `https://lift02-xyz123.vercel.app`
4. Only deploy to production after explicit approval
5. Production is controlled from `main` branch only

### Environment Variables Setup

Set these environment variables in Vercel dashboard (Settings > Environment Variables):
Expand Down
40 changes: 30 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"prettier-plugin-sql": "^0.19.1",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.12",
"supabase": "^2.40.7",
"supabase": "^2.53.6",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"svelte-hero-icons": "^5.2.0",
Expand All @@ -51,6 +51,7 @@
},
"dependencies": {
"@fontsource/metropolis": "^5.2.5",
"@supabase/ssr": "^0.7.0",
"@supabase/supabase-js": "^2.49.8",
"@tailwindcss/vite": "^4.1.10",
"daisyui": "^5.0.43",
Expand Down
11 changes: 7 additions & 4 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ echo "📝 Committing version change..."
git add "package.json" "src/lib/version.ts"
git commit -m "chore: bump version to $NEW_VERSION" || echo "⚠️ No changes to commit"

echo "🏗️ Building and deploying to production..."
echo "🏗️ Building and deploying to PREVIEW (not production)..."

# Deploy to Vercel
npx vercel --prod
# Deploy to Vercel PREVIEW (not production)
# IMPORTANT: We deploy to preview only. Production is manually controlled.
npx vercel

echo "🎉 Deployment completed! New version: $NEW_VERSION"
echo "🔗 Check your deployment at: https://lift02.vercel.app"
echo "⚠️ This was deployed as a PREVIEW deployment (not production)"
echo "🔗 Check the deployment URL from the vercel output above"
echo "📌 To deploy to production, use: npx vercel --prod (manual only!)"
52 changes: 28 additions & 24 deletions scripts/generate-test-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,17 @@ cat > supabase/generated/test_fake_data.sql << 'EOF'
-- DO NOT EDIT MANUALLY - run scripts/generate-test-data.sh to regenerate

-- ===========================================
-- ORGANIZATIONS
-- ORGANIZATIONS (skipped - using simplified line manager fields instead)
-- ===========================================
-- We now use line_manager_name and line_manager_email fields directly on profiles
-- The old organizations and line_managers tables are kept for backward compatibility
-- but not populated in test data

EOF

# Add organizations
echo "$TEST_DATA" | jq -r '.organizations[] |
"INSERT INTO organizations (id, name) VALUES (\u0027" + .id + "\u0027::uuid, \u0027" + .name + "\u0027);"' >> supabase/generated/test_fake_data.sql
# Skip organizations - we're using simplified approach
# echo "$TEST_DATA" | jq -r '.organizations[] |
# "INSERT INTO organizations (id, name) VALUES (\u0027" + .id + "\u0027::uuid, \u0027" + .name + "\u0027);"' >> supabase/generated/test_fake_data.sql

echo "" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
Expand Down Expand Up @@ -135,36 +138,37 @@ echo "$TEST_DATA" | jq -r '.users[] |

echo "" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
echo "-- PROFILES (step 1: without line_manager FK)" >> supabase/generated/test_fake_data.sql
echo "-- PROFILES (auto-created by trigger, update with additional fields)" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
echo "" >> supabase/generated/test_fake_data.sql

# Add profiles without line_manager FK first
echo "INSERT INTO profiles (id, user_id, name, pronouns, job_title, is_line_manager) VALUES" >> supabase/generated/test_fake_data.sql

echo "$TEST_DATA" | jq -r '.users | length as $len | to_entries | map(
" (\u0027" + .value.id + "\u0027::uuid, \u0027" + .value.id + "\u0027::uuid, \u0027" + .value.name + "\u0027, ARRAY[" + (.value.pronouns | map("\u0027" + . + "\u0027") | join(", ")) + "], \u0027" + .value.job_title + "\u0027, " + (.value.is_line_manager | tostring) + ")" + (if (.key == ($len - 1)) then ";" else "," end)
) | join("\n")' >> supabase/generated/test_fake_data.sql
# Update profiles created by trigger with additional fields
echo "$TEST_DATA" | jq -r '.users[] |
"UPDATE profiles SET
name = \u0027" + .name + "\u0027,
pronouns = ARRAY[" + (.pronouns | map("\u0027" + . + "\u0027") | join(", ")) + "],
job_title = \u0027" + .job_title + "\u0027,
is_line_manager = " + (.is_line_manager | tostring) + ",
line_manager_name = " + (if .line_manager_name then "\u0027" + .line_manager_name + "\u0027" else "NULL" end) + ",
line_manager_email = " + (if .line_manager_email then "\u0027" + .line_manager_email + "\u0027" else "NULL" end) + "
WHERE user_id = \u0027" + .id + "\u0027::uuid;"' >> supabase/generated/test_fake_data.sql

echo "" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
echo "-- LINE MANAGERS" >> supabase/generated/test_fake_data.sql
echo "-- LINE MANAGERS (skipped - using simplified approach)" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
echo "-- We now store line manager info directly on profiles via:" >> supabase/generated/test_fake_data.sql
echo "-- - line_manager_name (TEXT)" >> supabase/generated/test_fake_data.sql
echo "-- - line_manager_email (TEXT)" >> supabase/generated/test_fake_data.sql
echo "" >> supabase/generated/test_fake_data.sql

# Add line managers
echo "$TEST_DATA" | jq -r '.line_managers[] |
"INSERT INTO line_managers (id, line_manager_id, organization_id, email) VALUES (\u0027" + .id + "\u0027::uuid, \u0027" + .line_manager_id + "\u0027::uuid, \u0027" + .organization_id + "\u0027::uuid, \u0027" + .email + "\u0027);"' >> supabase/generated/test_fake_data.sql

echo "" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
echo "-- PROFILES (step 2: update line_manager FK)" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
echo "" >> supabase/generated/test_fake_data.sql
# Skip line_managers table - we're using simplified approach
# echo "$TEST_DATA" | jq -r '.line_managers[] |
# "INSERT INTO line_managers (id, line_manager_id, organization_id, email) VALUES (\u0027" + .id + "\u0027::uuid, \u0027" + .line_manager_id + "\u0027::uuid, \u0027" + .organization_id + "\u0027::uuid, \u0027" + .email + "\u0027);"' >> supabase/generated/test_fake_data.sql

# Update profiles to set line_manager FK
echo "$TEST_DATA" | jq -r '.users[] | select(.line_manager != null) |
"UPDATE profiles SET line_manager = \u0027" + .line_manager + "\u0027::uuid WHERE id = \u0027" + .id + "\u0027::uuid;"' >> supabase/generated/test_fake_data.sql
# Skip old line_manager FK update
# echo "$TEST_DATA" | jq -r '.users[] | select(.line_manager != null) |
# "UPDATE profiles SET line_manager = \u0027" + .line_manager + "\u0027::uuid WHERE id = \u0027" + .id + "\u0027::uuid;"' >> supabase/generated/test_fake_data.sql

echo "" >> supabase/generated/test_fake_data.sql
echo "-- ===========================================" >> supabase/generated/test_fake_data.sql
Expand Down
5 changes: 4 additions & 1 deletion scripts/prod-seed-test-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ PROFILES_JSON=$(echo "$TEST_DATA" | jq '[.users[] | {
name: .name,
pronouns: .pronouns,
job_title: .job_title,
is_line_manager: .is_line_manager
is_line_manager: .is_line_manager,
email: .email,
line_manager_name: .line_manager_name,
line_manager_email: .line_manager_email
}]')

PROFILE_RESPONSE=$(curl -s -X POST "$API_URL/profiles" \
Expand Down
11 changes: 9 additions & 2 deletions src/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
import type { Session, SupabaseClient } from '@supabase/supabase-js';

declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
interface Locals {
supabase: SupabaseClient;
getSession: () => Promise<Session | null>;
}
interface PageData {
session: Session | null;
}
// interface PageState {}
// interface Platform {}
}
Expand Down
34 changes: 34 additions & 0 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createServerClient } from '@supabase/ssr';
import { type Handle } from '@sveltejs/kit';
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';

export const handle: Handle = async ({ event, resolve }) => {
event.locals.supabase = createServerClient(
PUBLIC_SUPABASE_URL,
PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
get: (key) => event.cookies.get(key),
set: (key, value, options) => {
event.cookies.set(key, value, { ...options, path: '/' });
},
remove: (key, options) => {
event.cookies.delete(key, { ...options, path: '/' });
}
}
}
);

event.locals.getSession = async () => {
const {
data: { session }
} = await event.locals.supabase.auth.getSession();
return session;
};

return resolve(event, {
filterSerializedResponseHeaders(name) {
return name === 'content-range';
}
});
};
43 changes: 23 additions & 20 deletions src/lib/components/cards/QuestionCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ToggleStatus from '../ui/ToggleStatus.svelte';
import SaveStatus from '../ui/SaveStatus.svelte';
import UndoButton from '../ui/UndoButton.svelte';
import Tooltip from '../ui/Tooltip.svelte';
import type { QuestionConnections, RowId, ViewName } from '$lib/types/appState';
import { getQuestionConnections } from '$lib/utils/getContent.svelte';
import { getContext } from 'svelte';
Expand Down Expand Up @@ -296,19 +297,20 @@
></textarea>

<div class="mt-2 flex items-center gap-2">
<button
onclick={saveResponse}
disabled={!hasChanges || isSaving}
class="btn-submit btn-sm"
title="Save response to database"
>
{#if isSaving}
<span class="loading loading-spinner loading-xs"></span>
Saving...
{:else}
OK
{/if}
</button>
<Tooltip text="Save response to database" position="top_right">
<button
onclick={saveResponse}
disabled={!hasChanges || isSaving}
class="btn-submit btn-sm"
>
{#if isSaving}
<span class="loading loading-spinner loading-xs"></span>
Saving...
{:else}
OK
{/if}
</button>
</Tooltip>
<UndoButton {canUndo} onclick={handleUndo} />
</div>
{#if saveError}
Expand All @@ -324,13 +326,14 @@

{#if connectionDetails.responseId}
<div class="mx-4 mt-2 mb-4 flex justify-end">
<button
onclick={openDeleteModal}
class="btn btn-error btn-sm"
title="Delete this response and all related actions"
>
Delete Response & Actions
</button>
<Tooltip text="Delete this response and all related actions" position="left">
<button
onclick={openDeleteModal}
class="btn btn-error btn-sm"
>
Delete Response & Actions
</button>
</Tooltip>
</div>
{/if}
{:else}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/components/layouts/Footer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { getContext } from 'svelte';
import FontSizeControl from '../ui/FontSizeControl.svelte';
import HelpButton from '../ui/HelpButton.svelte';
import ProfileButton from '../ui/ProfileButton.svelte';

let { devMode } = $props();

Expand All @@ -16,6 +17,8 @@
<footer class="footer">
<HelpButton />

<ProfileButton />

<FontSizeControl />
</footer>

Expand Down
Loading