diff --git a/ROADMAP.md b/ROADMAP.md index 3efb24a..fcf87f0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -448,6 +448,16 @@ Kabul kriteri: - Kullanici sadece kendi verisini gorur. - Demo data ile dashboard beslenebilir. +Faz 1 repo ciktisi: + +- Core schema migration: `supabase/migrations/0002_add_freelancer_os_core_tables.sql` +- Demo seed dosyasi: `supabase/seeds/0001_demo_freelancer_os_data.sql` +- Migration dokumani: `docs/database/0002-freelancer-os-core-tables.md` +- Seed dokumani: `docs/database/seed-0001-demo-freelancer-os-data.md` +- Query sirasi kaydi: `docs/database/query-order.md` + +Not: SQL dosyalari repo icinde hazirdir. Supabase ortaminda calistirildiktan sonra `docs/database/query-log.md` guncellenmelidir. + --- ### Faz 2: Core CRUD Modulleri diff --git a/docs/database/0002-freelancer-os-core-tables.md b/docs/database/0002-freelancer-os-core-tables.md new file mode 100644 index 0000000..812d276 --- /dev/null +++ b/docs/database/0002-freelancer-os-core-tables.md @@ -0,0 +1,72 @@ +# 0002 Freelancer OS Core Tables + +SQL file: `supabase/migrations/0002_add_freelancer_os_core_tables.sql` + +## Purpose + +Adds the database model required for the Freelancer OS MVP. + +This migration keeps the existing baseline from `supabase/schema.sql` and extends it instead of replacing it. The existing `tasks` table is reused and enriched with freelancer-specific fields. + +## What It Creates + +- `clients` +- `projects` +- `calendar_events` +- `finance_transactions` +- `daily_logs` +- `app_settings` +- `set_updated_at()` trigger function +- updated-at triggers for the new tables +- indexes for common dashboard/report queries +- RLS policies for every new table + +## What It Changes + +The existing `tasks` table gets these additional columns: + +- `client_id` +- `project_id` +- `priority` +- `due_at` +- `estimated_minutes` +- `actual_minutes` + +This lets a task belong to a client and/or project while preserving the earlier journal/task prototype schema. + +## MVP Coverage + +This migration supports: + +- client management +- client projects +- side projects +- task planning +- calendar planning +- income and expense tracking +- daily mood/energy logging +- app and AI provider settings + +## RLS Model + +Every new table has `user_id`. + +Policies follow the same pattern: + +- users can select their own rows +- users can insert rows only for themselves +- users can update their own rows +- users can delete their own rows + +## Notes + +`app_settings.api_key` exists for compatibility with the current prototype flow. For production-grade use, provider credentials should be encrypted or moved to a safer secret-management strategy. + +`daily_logs` is the new MVP-oriented replacement for the earlier `journals` concept. The old `journals` table remains available until the UI migration is complete. + +## Execution + +Run this after `supabase/schema.sql`. + +Do not add this file to `query-log.md` until it has actually been executed against a database. + diff --git a/docs/database/README.md b/docs/database/README.md index 6044b55..f714ebc 100644 --- a/docs/database/README.md +++ b/docs/database/README.md @@ -15,7 +15,11 @@ The project currently starts with `supabase/schema.sql` as the first database qu ## Current Files - `supabase/schema.sql`: Initial legacy schema. It creates the current auth/profile, journal, task, chat, and avatar storage structure. +- `supabase/migrations/0002_add_freelancer_os_core_tables.sql`: Freelancer OS MVP core schema. +- `supabase/seeds/0001_demo_freelancer_os_data.sql`: Optional local/demo data for the MVP schema. - `docs/database/0001-initial-schema.md`: Explanation for the initial schema. +- `docs/database/0002-freelancer-os-core-tables.md`: Explanation for the MVP schema migration. +- `docs/database/seed-0001-demo-freelancer-os-data.md`: Explanation for the demo seed file. - `docs/database/query-order.md`: Canonical order of SQL files. - `docs/database/query-log.md`: Manual execution log for SQL files that were run against an environment. @@ -33,3 +37,8 @@ Example: supabase/migrations/0002_add_freelancer_os_core_tables.sql ``` +Use this pattern for future seed files: + +```text +supabase/seeds/0002_short_description.sql +``` diff --git a/docs/database/query-order.md b/docs/database/query-order.md index 6e518db..77cd21f 100644 --- a/docs/database/query-order.md +++ b/docs/database/query-order.md @@ -5,6 +5,8 @@ This file is the canonical order of SQL files for database setup and migration. | Order | SQL file | Documentation | Status | | --- | --- | --- | --- | | 0001 | `supabase/schema.sql` | `docs/database/0001-initial-schema.md` | Baseline registered | +| 0002 | `supabase/migrations/0002_add_freelancer_os_core_tables.sql` | `docs/database/0002-freelancer-os-core-tables.md` | Pending execution | +| seed-0001 | `supabase/seeds/0001_demo_freelancer_os_data.sql` | `docs/database/seed-0001-demo-freelancer-os-data.md` | Optional demo seed, pending execution | ## How To Add The Next Query @@ -14,3 +16,8 @@ This file is the canonical order of SQL files for database setup and migration. 4. Register both files in this table. 5. After running the SQL, add an entry to `query-log.md`. +## Seed Files + +Seed files are optional and should live under `supabase/seeds/`. + +They must also be documented and registered in this file, but they should only be run in local/demo environments unless explicitly approved. diff --git a/docs/database/seed-0001-demo-freelancer-os-data.md b/docs/database/seed-0001-demo-freelancer-os-data.md new file mode 100644 index 0000000..fa2ee33 --- /dev/null +++ b/docs/database/seed-0001-demo-freelancer-os-data.md @@ -0,0 +1,43 @@ +# Seed 0001 Demo Freelancer OS Data + +SQL file: `supabase/seeds/0001_demo_freelancer_os_data.sql` + +## Purpose + +Adds demo data for local development and dashboard testing after the Freelancer OS core tables are created. + +## What It Inserts + +- demo clients +- demo projects and one side project +- demo tasks +- demo calendar events +- demo finance transactions +- demo daily mood/energy logs +- demo `app_settings` + +## Required Manual Step + +Before running the file, replace this placeholder with a real `auth.users.id`: + +```sql +'00000000-0000-0000-0000-000000000000'::uuid +``` + +Use a user id from your Supabase Auth users table. + +## Environment + +This seed is intended for local and demo environments only. + +Do not run it on production data. + +## Execution + +Run after: + +1. `supabase/schema.sql` +2. `supabase/migrations/0002_add_freelancer_os_core_tables.sql` + +After running it, add an entry to `docs/database/query-log.md`. + diff --git a/supabase/README.md b/supabase/README.md index 516ecda..f70aa62 100644 --- a/supabase/README.md +++ b/supabase/README.md @@ -17,5 +17,16 @@ Future schema changes should be added under: supabase/migrations/ ``` +Optional local/demo data should be added under: + +```text +supabase/seeds/ +``` + Do not overwrite already executed SQL without also creating a new ordered migration and documenting why. +## Current Ordered SQL + +1. `schema.sql` +2. `migrations/0002_add_freelancer_os_core_tables.sql` +3. Optional: `seeds/0001_demo_freelancer_os_data.sql` diff --git a/supabase/migrations/0002_add_freelancer_os_core_tables.sql b/supabase/migrations/0002_add_freelancer_os_core_tables.sql new file mode 100644 index 0000000..4fa72e1 --- /dev/null +++ b/supabase/migrations/0002_add_freelancer_os_core_tables.sql @@ -0,0 +1,336 @@ +-- 0002: Add Freelancer OS core tables +-- Run after: supabase/schema.sql + +create extension if not exists "uuid-ossp"; + +create or replace function public.set_updated_at() +returns trigger as $$ +begin + new.updated_at = timezone('utc'::text, now()); + return new; +end; +$$ language plpgsql; + +create table if not exists public.clients ( + id uuid default uuid_generate_v4() primary key, + user_id uuid references auth.users(id) on delete cascade not null, + name text not null, + company_name text, + email text, + phone text, + website text, + status text default 'active'::text not null, + notes text, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()) not null +); + +create table if not exists public.projects ( + id uuid default uuid_generate_v4() primary key, + user_id uuid references auth.users(id) on delete cascade not null, + client_id uuid references public.clients(id) on delete set null, + name text not null, + type text default 'client_project'::text not null, + description text, + status text default 'planning'::text not null, + start_date date, + due_date date, + budget_amount numeric(12, 2), + currency text default 'USD'::text not null, + progress integer default 0 not null, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()) not null +); + +create table if not exists public.calendar_events ( + id uuid default uuid_generate_v4() primary key, + user_id uuid references auth.users(id) on delete cascade not null, + client_id uuid references public.clients(id) on delete set null, + project_id uuid references public.projects(id) on delete set null, + task_id uuid references public.tasks(id) on delete set null, + title text not null, + description text, + type text default 'focus'::text not null, + starts_at timestamp with time zone not null, + ends_at timestamp with time zone, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()) not null +); + +create table if not exists public.finance_transactions ( + id uuid default uuid_generate_v4() primary key, + user_id uuid references auth.users(id) on delete cascade not null, + client_id uuid references public.clients(id) on delete set null, + project_id uuid references public.projects(id) on delete set null, + type text not null, + amount numeric(12, 2) not null, + currency text default 'USD'::text not null, + transaction_date date default current_date not null, + category text, + payment_status text default 'planned'::text not null, + description text, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()) not null +); + +create table if not exists public.daily_logs ( + id uuid default uuid_generate_v4() primary key, + user_id uuid references auth.users(id) on delete cascade not null, + log_date date default current_date not null, + mood_score smallint not null, + energy_score smallint not null, + work_satisfaction_score smallint, + note text, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()) not null, + unique (user_id, log_date) +); + +create table if not exists public.app_settings ( + id uuid default uuid_generate_v4() primary key, + user_id uuid references auth.users(id) on delete cascade not null unique, + timezone text default 'UTC'::text not null, + currency text default 'USD'::text not null, + ai_provider text default 'ollama'::text not null, + ai_model text, + api_key text, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()) not null +); + +alter table public.tasks + add column if not exists client_id uuid references public.clients(id) on delete set null, + add column if not exists project_id uuid references public.projects(id) on delete set null, + add column if not exists priority text default 'medium'::text not null, + add column if not exists due_at timestamp with time zone, + add column if not exists estimated_minutes integer, + add column if not exists actual_minutes integer; + +do $$ +begin + if not exists (select 1 from pg_constraint where conname = 'clients_status_check') then + alter table public.clients + add constraint clients_status_check + check (status in ('active', 'paused', 'archived')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'projects_type_check') then + alter table public.projects + add constraint projects_type_check + check (type in ('client_project', 'side_project')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'projects_status_check') then + alter table public.projects + add constraint projects_status_check + check (status in ('planning', 'active', 'paused', 'completed', 'cancelled')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'projects_progress_check') then + alter table public.projects + add constraint projects_progress_check + check (progress >= 0 and progress <= 100); + end if; + + if not exists (select 1 from pg_constraint where conname = 'tasks_priority_check') then + alter table public.tasks + add constraint tasks_priority_check + check (priority in ('low', 'medium', 'high', 'urgent')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'calendar_events_type_check') then + alter table public.calendar_events + add constraint calendar_events_type_check + check (type in ('meeting', 'focus', 'deadline', 'personal', 'finance')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'calendar_events_time_check') then + alter table public.calendar_events + add constraint calendar_events_time_check + check (ends_at is null or ends_at >= starts_at); + end if; + + if not exists (select 1 from pg_constraint where conname = 'finance_transactions_type_check') then + alter table public.finance_transactions + add constraint finance_transactions_type_check + check (type in ('income', 'expense')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'finance_transactions_amount_check') then + alter table public.finance_transactions + add constraint finance_transactions_amount_check + check (amount >= 0); + end if; + + if not exists (select 1 from pg_constraint where conname = 'finance_transactions_payment_status_check') then + alter table public.finance_transactions + add constraint finance_transactions_payment_status_check + check (payment_status in ('planned', 'pending', 'paid', 'cancelled')); + end if; + + if not exists (select 1 from pg_constraint where conname = 'daily_logs_mood_score_check') then + alter table public.daily_logs + add constraint daily_logs_mood_score_check + check (mood_score between 1 and 5); + end if; + + if not exists (select 1 from pg_constraint where conname = 'daily_logs_energy_score_check') then + alter table public.daily_logs + add constraint daily_logs_energy_score_check + check (energy_score between 1 and 5); + end if; + + if not exists (select 1 from pg_constraint where conname = 'daily_logs_work_satisfaction_score_check') then + alter table public.daily_logs + add constraint daily_logs_work_satisfaction_score_check + check (work_satisfaction_score is null or work_satisfaction_score between 1 and 5); + end if; + + if not exists (select 1 from pg_constraint where conname = 'app_settings_ai_provider_check') then + alter table public.app_settings + add constraint app_settings_ai_provider_check + check (ai_provider in ('ollama', 'openai', 'gemini', 'groq')); + end if; +end $$; + +create index if not exists clients_user_id_idx on public.clients(user_id); +create index if not exists clients_status_idx on public.clients(status); + +create index if not exists projects_user_id_idx on public.projects(user_id); +create index if not exists projects_client_id_idx on public.projects(client_id); +create index if not exists projects_status_idx on public.projects(status); +create index if not exists projects_due_date_idx on public.projects(due_date); + +create index if not exists tasks_client_id_idx on public.tasks(client_id); +create index if not exists tasks_project_id_idx on public.tasks(project_id); +create index if not exists tasks_due_at_idx on public.tasks(due_at); +create index if not exists tasks_priority_idx on public.tasks(priority); + +create index if not exists calendar_events_user_id_idx on public.calendar_events(user_id); +create index if not exists calendar_events_starts_at_idx on public.calendar_events(starts_at); +create index if not exists calendar_events_project_id_idx on public.calendar_events(project_id); +create index if not exists calendar_events_task_id_idx on public.calendar_events(task_id); + +create index if not exists finance_transactions_user_id_idx on public.finance_transactions(user_id); +create index if not exists finance_transactions_client_id_idx on public.finance_transactions(client_id); +create index if not exists finance_transactions_project_id_idx on public.finance_transactions(project_id); +create index if not exists finance_transactions_transaction_date_idx on public.finance_transactions(transaction_date); +create index if not exists finance_transactions_payment_status_idx on public.finance_transactions(payment_status); + +create index if not exists daily_logs_user_id_idx on public.daily_logs(user_id); +create index if not exists daily_logs_log_date_idx on public.daily_logs(log_date); + +alter table public.clients enable row level security; +alter table public.projects enable row level security; +alter table public.calendar_events enable row level security; +alter table public.finance_transactions enable row level security; +alter table public.daily_logs enable row level security; +alter table public.app_settings enable row level security; + +drop policy if exists "Users can view their own clients." on public.clients; +create policy "Users can view their own clients." on public.clients + for select using (auth.uid() = user_id); +drop policy if exists "Users can insert their own clients." on public.clients; +create policy "Users can insert their own clients." on public.clients + for insert with check (auth.uid() = user_id); +drop policy if exists "Users can update their own clients." on public.clients; +create policy "Users can update their own clients." on public.clients + for update using (auth.uid() = user_id); +drop policy if exists "Users can delete their own clients." on public.clients; +create policy "Users can delete their own clients." on public.clients + for delete using (auth.uid() = user_id); + +drop policy if exists "Users can view their own projects." on public.projects; +create policy "Users can view their own projects." on public.projects + for select using (auth.uid() = user_id); +drop policy if exists "Users can insert their own projects." on public.projects; +create policy "Users can insert their own projects." on public.projects + for insert with check (auth.uid() = user_id); +drop policy if exists "Users can update their own projects." on public.projects; +create policy "Users can update their own projects." on public.projects + for update using (auth.uid() = user_id); +drop policy if exists "Users can delete their own projects." on public.projects; +create policy "Users can delete their own projects." on public.projects + for delete using (auth.uid() = user_id); + +drop policy if exists "Users can view their own calendar events." on public.calendar_events; +create policy "Users can view their own calendar events." on public.calendar_events + for select using (auth.uid() = user_id); +drop policy if exists "Users can insert their own calendar events." on public.calendar_events; +create policy "Users can insert their own calendar events." on public.calendar_events + for insert with check (auth.uid() = user_id); +drop policy if exists "Users can update their own calendar events." on public.calendar_events; +create policy "Users can update their own calendar events." on public.calendar_events + for update using (auth.uid() = user_id); +drop policy if exists "Users can delete their own calendar events." on public.calendar_events; +create policy "Users can delete their own calendar events." on public.calendar_events + for delete using (auth.uid() = user_id); + +drop policy if exists "Users can view their own finance transactions." on public.finance_transactions; +create policy "Users can view their own finance transactions." on public.finance_transactions + for select using (auth.uid() = user_id); +drop policy if exists "Users can insert their own finance transactions." on public.finance_transactions; +create policy "Users can insert their own finance transactions." on public.finance_transactions + for insert with check (auth.uid() = user_id); +drop policy if exists "Users can update their own finance transactions." on public.finance_transactions; +create policy "Users can update their own finance transactions." on public.finance_transactions + for update using (auth.uid() = user_id); +drop policy if exists "Users can delete their own finance transactions." on public.finance_transactions; +create policy "Users can delete their own finance transactions." on public.finance_transactions + for delete using (auth.uid() = user_id); + +drop policy if exists "Users can view their own daily logs." on public.daily_logs; +create policy "Users can view their own daily logs." on public.daily_logs + for select using (auth.uid() = user_id); +drop policy if exists "Users can insert their own daily logs." on public.daily_logs; +create policy "Users can insert their own daily logs." on public.daily_logs + for insert with check (auth.uid() = user_id); +drop policy if exists "Users can update their own daily logs." on public.daily_logs; +create policy "Users can update their own daily logs." on public.daily_logs + for update using (auth.uid() = user_id); +drop policy if exists "Users can delete their own daily logs." on public.daily_logs; +create policy "Users can delete their own daily logs." on public.daily_logs + for delete using (auth.uid() = user_id); + +drop policy if exists "Users can view their own app settings." on public.app_settings; +create policy "Users can view their own app settings." on public.app_settings + for select using (auth.uid() = user_id); +drop policy if exists "Users can insert their own app settings." on public.app_settings; +create policy "Users can insert their own app settings." on public.app_settings + for insert with check (auth.uid() = user_id); +drop policy if exists "Users can update their own app settings." on public.app_settings; +create policy "Users can update their own app settings." on public.app_settings + for update using (auth.uid() = user_id); +drop policy if exists "Users can delete their own app settings." on public.app_settings; +create policy "Users can delete their own app settings." on public.app_settings + for delete using (auth.uid() = user_id); + +drop trigger if exists set_clients_updated_at on public.clients; +create trigger set_clients_updated_at + before update on public.clients + for each row execute procedure public.set_updated_at(); + +drop trigger if exists set_projects_updated_at on public.projects; +create trigger set_projects_updated_at + before update on public.projects + for each row execute procedure public.set_updated_at(); + +drop trigger if exists set_calendar_events_updated_at on public.calendar_events; +create trigger set_calendar_events_updated_at + before update on public.calendar_events + for each row execute procedure public.set_updated_at(); + +drop trigger if exists set_finance_transactions_updated_at on public.finance_transactions; +create trigger set_finance_transactions_updated_at + before update on public.finance_transactions + for each row execute procedure public.set_updated_at(); + +drop trigger if exists set_daily_logs_updated_at on public.daily_logs; +create trigger set_daily_logs_updated_at + before update on public.daily_logs + for each row execute procedure public.set_updated_at(); + +drop trigger if exists set_app_settings_updated_at on public.app_settings; +create trigger set_app_settings_updated_at + before update on public.app_settings + for each row execute procedure public.set_updated_at(); diff --git a/supabase/seeds/0001_demo_freelancer_os_data.sql b/supabase/seeds/0001_demo_freelancer_os_data.sql new file mode 100644 index 0000000..cee2f31 --- /dev/null +++ b/supabase/seeds/0001_demo_freelancer_os_data.sql @@ -0,0 +1,210 @@ +-- Seed 0001: Demo Freelancer OS data +-- Run after: supabase/migrations/0002_add_freelancer_os_core_tables.sql +-- +-- Before running, replace the UUID below with an existing auth.users.id. +-- This file is for local/demo environments. Do not run it on production data. + +with seed_user as ( + select '00000000-0000-0000-0000-000000000000'::uuid as user_id +), +demo_clients as ( + insert into public.clients (user_id, name, company_name, email, status, notes) + select user_id, 'Acme Studio', 'Acme Studio LLC', 'hello@acme.example', 'active', 'Primary design and web retainer client.' + from seed_user + union all + select user_id, 'Northwind Labs', 'Northwind Labs', 'ops@northwind.example', 'active', 'Product strategy project with monthly milestones.' + from seed_user + returning id, user_id, name +), +demo_projects as ( + insert into public.projects (user_id, client_id, name, type, description, status, start_date, due_date, budget_amount, currency, progress) + select + c.user_id, + c.id, + 'Acme Website Refresh', + 'client_project', + 'Marketing website refresh and conversion improvements.', + 'active', + current_date - 14, + current_date + 21, + 4200, + 'USD', + 45 + from demo_clients c + where c.name = 'Acme Studio' + union all + select + c.user_id, + c.id, + 'Northwind Product Audit', + 'client_project', + 'Audit onboarding, analytics, and project roadmap.', + 'active', + current_date - 7, + current_date + 14, + 2800, + 'USD', + 30 + from demo_clients c + where c.name = 'Northwind Labs' + union all + select + user_id, + null, + 'Cognis Open Source Launch', + 'side_project', + 'Prepare the self-hosted Freelancer OS MVP for public release.', + 'planning', + current_date, + current_date + 45, + null, + 'USD', + 10 + from seed_user + returning id, user_id, client_id, name +), +demo_tasks as ( + insert into public.tasks (user_id, client_id, project_id, title, description, status, priority, due_at, estimated_minutes, actual_minutes) + select + p.user_id, + p.client_id, + p.id, + 'Prepare homepage wireframe', + 'Create first-pass homepage structure for client review.', + 'todo', + 'high', + timezone('utc'::text, now()) + interval '2 days', + 180, + null + from demo_projects p + where p.name = 'Acme Website Refresh' + union all + select + p.user_id, + p.client_id, + p.id, + 'Review onboarding analytics', + 'Summarize funnel drop-off points and quick wins.', + 'in_progress', + 'urgent', + timezone('utc'::text, now()) + interval '1 day', + 120, + 45 + from demo_projects p + where p.name = 'Northwind Product Audit' + union all + select + p.user_id, + p.client_id, + p.id, + 'Write MVP installation notes', + 'Document self-host setup, env vars, and migration order.', + 'todo', + 'medium', + timezone('utc'::text, now()) + interval '5 days', + 90, + null + from demo_projects p + where p.name = 'Cognis Open Source Launch' + returning id, user_id, client_id, project_id, title +), +demo_events as ( + insert into public.calendar_events (user_id, client_id, project_id, task_id, title, description, type, starts_at, ends_at) + select + t.user_id, + t.client_id, + t.project_id, + t.id, + 'Client review: homepage wireframe', + 'Review initial direction with Acme.', + 'meeting', + timezone('utc'::text, now()) + interval '3 days', + timezone('utc'::text, now()) + interval '3 days 1 hour' + from demo_tasks t + where t.title = 'Prepare homepage wireframe' + union all + select + t.user_id, + t.client_id, + t.project_id, + t.id, + 'Deep work: analytics audit', + 'Focus block for Northwind analysis.', + 'focus', + timezone('utc'::text, now()) + interval '1 day', + timezone('utc'::text, now()) + interval '1 day 2 hours' + from demo_tasks t + where t.title = 'Review onboarding analytics' + returning id +), +demo_finance as ( + insert into public.finance_transactions (user_id, client_id, project_id, type, amount, currency, transaction_date, category, payment_status, description) + select + p.user_id, + p.client_id, + p.id, + 'income', + 2100, + 'USD', + current_date - 3, + 'Project deposit', + 'paid', + 'Acme Website Refresh initial payment.' + from demo_projects p + where p.name = 'Acme Website Refresh' + union all + select + p.user_id, + p.client_id, + p.id, + 'income', + 2800, + 'USD', + current_date + 10, + 'Project payment', + 'pending', + 'Northwind audit payment due.' + from demo_projects p + where p.name = 'Northwind Product Audit' + union all + select + user_id, + null, + null, + 'expense', + 49, + 'USD', + current_date, + 'Software', + 'paid', + 'Monthly tool subscription.' + from seed_user + returning id +), +demo_daily_logs as ( + insert into public.daily_logs (user_id, log_date, mood_score, energy_score, work_satisfaction_score, note) + select user_id, current_date - 2, 3, 3, 3, 'Mixed admin day with some client context switching.' + from seed_user + union all + select user_id, current_date - 1, 4, 5, 4, 'Strong focus block and clear project progress.' + from seed_user + union all + select user_id, current_date, 4, 4, 4, 'Good planning momentum for the week.' + from seed_user + on conflict (user_id, log_date) do update set + mood_score = excluded.mood_score, + energy_score = excluded.energy_score, + work_satisfaction_score = excluded.work_satisfaction_score, + note = excluded.note, + updated_at = timezone('utc'::text, now()) + returning id +) +insert into public.app_settings (user_id, timezone, currency, ai_provider, ai_model) +select user_id, 'UTC', 'USD', 'ollama', 'llama3' +from seed_user +on conflict (user_id) do update set + timezone = excluded.timezone, + currency = excluded.currency, + ai_provider = excluded.ai_provider, + ai_model = excluded.ai_model, + updated_at = timezone('utc'::text, now());