feat: add core database schema and demo seed data for Freelancer OS MVP
This commit is contained in:
+10
@@ -448,6 +448,16 @@ Kabul kriteri:
|
|||||||
- Kullanici sadece kendi verisini gorur.
|
- Kullanici sadece kendi verisini gorur.
|
||||||
- Demo data ile dashboard beslenebilir.
|
- 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
|
### Faz 2: Core CRUD Modulleri
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
@@ -15,7 +15,11 @@ The project currently starts with `supabase/schema.sql` as the first database qu
|
|||||||
## Current Files
|
## Current Files
|
||||||
|
|
||||||
- `supabase/schema.sql`: Initial legacy schema. It creates the current auth/profile, journal, task, chat, and avatar storage structure.
|
- `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/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-order.md`: Canonical order of SQL files.
|
||||||
- `docs/database/query-log.md`: Manual execution log for SQL files that were run against an environment.
|
- `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
|
supabase/migrations/0002_add_freelancer_os_core_tables.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Use this pattern for future seed files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
supabase/seeds/0002_short_description.sql
|
||||||
|
```
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ This file is the canonical order of SQL files for database setup and migration.
|
|||||||
| Order | SQL file | Documentation | Status |
|
| Order | SQL file | Documentation | Status |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| 0001 | `supabase/schema.sql` | `docs/database/0001-initial-schema.md` | Baseline registered |
|
| 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
|
## 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.
|
4. Register both files in this table.
|
||||||
5. After running the SQL, add an entry to `query-log.md`.
|
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.
|
||||||
|
|||||||
@@ -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`.
|
||||||
|
|
||||||
@@ -17,5 +17,16 @@ Future schema changes should be added under:
|
|||||||
supabase/migrations/
|
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.
|
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`
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -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());
|
||||||
Reference in New Issue
Block a user