627645056b
Separate feature for the `programmer` role to record dev work by category, distinct from the IT help-desk `tasks` feature. Modeled on the IT Service Requests pattern (Brick offline-first model -> Riverpod stream/controller -> list/detail screens); seed of a future Project Management workflow. - programmer_tasks + programmer_task_activity_logs (category enum, RLS, realtime) via supabase migration - single assignee, self-logged or lead-assigned; assign/reassign logged - pause-on-switch: starting a task while another runs prompts to pause the running one, keeping one active task per user for accurate time attribution - worked-duration math shared with help-desk tasks via a generic core in task_duration.dart (existing callers unchanged) - list distinguishes running vs paused with a per-task Resume shortcut - accessible dialog labels; route /programmer-tasks + nav gated to admin/programmer - unit/widget tests: controller lifecycle, duration math, pause-switch dialog Co-Authored-By: claude-flow <ruv@ruv.net>
169 lines
6.1 KiB
PL/PgSQL
169 lines
6.1 KiB
PL/PgSQL
-- Programmer Tasks — a separate work-log / time-tracking module for the
|
|
-- `programmer` role. Distinct from the IT help-desk `tasks` feature: no office/
|
|
-- ticket/it_staff coupling, its own dev-flavored category vocabulary, a single
|
|
-- assignee, and pause/resume tracked as activity-log events for time attribution.
|
|
-- Designed as the seed of a future Project Management workflow (projects/subtasks
|
|
-- can be added later without touching the help-desk `tasks` feature).
|
|
|
|
-- Category vocabulary (TitleCase display strings, matching the existing enum
|
|
-- convention used by request_type / request_category).
|
|
create type programmer_task_category as enum (
|
|
'Software Support',
|
|
'Software Development',
|
|
'Software Enhancement',
|
|
'Bug Fix',
|
|
'Meeting',
|
|
'Facilitate Event',
|
|
'Others'
|
|
);
|
|
|
|
create table if not exists programmer_tasks (
|
|
id uuid primary key default gen_random_uuid(),
|
|
title text not null,
|
|
description text,
|
|
category programmer_task_category not null,
|
|
status text not null default 'queued',
|
|
priority int not null default 1,
|
|
assignee_id uuid references profiles(id),
|
|
creator_id uuid references profiles(id),
|
|
created_at timestamptz not null default now(),
|
|
started_at timestamptz,
|
|
completed_at timestamptz,
|
|
cancelled_at timestamptz,
|
|
cancellation_reason text,
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
create index if not exists idx_programmer_tasks_assignee_id on programmer_tasks(assignee_id);
|
|
create index if not exists idx_programmer_tasks_status on programmer_tasks(status);
|
|
|
|
-- Keep updated_at fresh on every write (dedicated function to avoid clashing
|
|
-- with any generic trigger function elsewhere).
|
|
create or replace function set_programmer_tasks_updated_at()
|
|
returns trigger as $$
|
|
begin
|
|
new.updated_at = now();
|
|
return new;
|
|
end;
|
|
$$ language plpgsql;
|
|
|
|
drop trigger if exists trg_programmer_tasks_updated_at on programmer_tasks;
|
|
create trigger trg_programmer_tasks_updated_at
|
|
before update on programmer_tasks
|
|
for each row execute function set_programmer_tasks_updated_at();
|
|
|
|
-- Activity log — mirrors task_activity_logs. Records created/started/paused/
|
|
-- resumed/completed/cancelled/assigned/reassigned events. Pause/resume events
|
|
-- drive the effective-worked-duration math (see lib/utils/task_duration.dart).
|
|
create table if not exists programmer_task_activity_logs (
|
|
id uuid primary key default gen_random_uuid(),
|
|
task_id uuid not null references programmer_tasks(id) on delete cascade,
|
|
actor_id uuid references profiles(id),
|
|
action_type text not null,
|
|
meta jsonb,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create index if not exists idx_programmer_task_activity_logs_task_id
|
|
on programmer_task_activity_logs(task_id);
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Row Level Security
|
|
-- ---------------------------------------------------------------------------
|
|
-- Programmer tasks are a programmer-team tool: admins and programmers have full
|
|
-- access; a row's assignee or creator can always see/act on their own task.
|
|
|
|
alter table programmer_tasks enable row level security;
|
|
alter table programmer_task_activity_logs enable row level security;
|
|
|
|
drop policy if exists "programmer_tasks_select" on programmer_tasks;
|
|
create policy "programmer_tasks_select" on programmer_tasks
|
|
for select to authenticated
|
|
using (
|
|
assignee_id = auth.uid()
|
|
or creator_id = auth.uid()
|
|
or exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
);
|
|
|
|
drop policy if exists "programmer_tasks_insert" on programmer_tasks;
|
|
create policy "programmer_tasks_insert" on programmer_tasks
|
|
for insert to authenticated
|
|
with check (
|
|
creator_id = auth.uid()
|
|
and exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
);
|
|
|
|
drop policy if exists "programmer_tasks_update" on programmer_tasks;
|
|
create policy "programmer_tasks_update" on programmer_tasks
|
|
for update to authenticated
|
|
using (
|
|
assignee_id = auth.uid()
|
|
or creator_id = auth.uid()
|
|
or exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
)
|
|
with check (
|
|
assignee_id = auth.uid()
|
|
or creator_id = auth.uid()
|
|
or exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
);
|
|
|
|
drop policy if exists "programmer_tasks_delete" on programmer_tasks;
|
|
create policy "programmer_tasks_delete" on programmer_tasks
|
|
for delete to authenticated
|
|
using (
|
|
creator_id = auth.uid()
|
|
or exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
);
|
|
|
|
-- Activity logs: visible/writable to anyone who can see the parent task.
|
|
drop policy if exists "programmer_task_activity_logs_select" on programmer_task_activity_logs;
|
|
create policy "programmer_task_activity_logs_select" on programmer_task_activity_logs
|
|
for select to authenticated
|
|
using (
|
|
exists (
|
|
select 1 from programmer_tasks t
|
|
where t.id = task_id
|
|
and (
|
|
t.assignee_id = auth.uid()
|
|
or t.creator_id = auth.uid()
|
|
or exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
drop policy if exists "programmer_task_activity_logs_insert" on programmer_task_activity_logs;
|
|
create policy "programmer_task_activity_logs_insert" on programmer_task_activity_logs
|
|
for insert to authenticated
|
|
with check (
|
|
exists (
|
|
select 1 from programmer_tasks t
|
|
where t.id = task_id
|
|
and (
|
|
t.assignee_id = auth.uid()
|
|
or t.creator_id = auth.uid()
|
|
or exists (
|
|
select 1 from profiles p where p.id = auth.uid() and p.role in ('admin', 'programmer')
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Realtime — the Riverpod .stream() subscriptions require these tables in the
|
|
-- supabase_realtime publication.
|
|
-- ---------------------------------------------------------------------------
|
|
alter publication supabase_realtime add table programmer_tasks;
|
|
alter publication supabase_realtime add table programmer_task_activity_logs;
|