Compare commits

...

13 Commits

Author SHA1 Message Date
Gimanh 0936f51a24 Merge pull request #7 from Gimanh/fix/history-deletion
fix: delete history cascade with entity (goal, list, tasks)
2026-01-08 22:10:00 +01:00
Nikolai Giman c4c6d91456 fix: delete history cascade with entity (goal, list, tasks) 2026-01-08 22:08:23 +01:00
Gimanh 1ba13eef45 Merge pull request #5 from Gimanh/fix/load-more-in-kanban
fix: increase task limit in TasksRepository and handle columnId in Ka…
2026-01-05 20:01:40 +01:00
Nikolai Giman 8a67296673 fix: increase task limit in TasksRepository and handle columnId in KanbanBoard to fix load more issue 2026-01-05 20:00:18 +01:00
Gimanh 8fe3353c5f Merge pull request #4 from Gimanh/fix/subtask-add-ui-bug
fix: add subtasks to selectedTask
2026-01-04 23:58:58 +01:00
Nikolai Giman 61debcda11 fix: add subtasks to selectedTask 2026-01-04 23:57:36 +01:00
Gimanh 615a47eafd Merge pull request #3 from Gimanh/chore/build
chore: update build scripts and package configurations
2026-01-04 22:04:12 +01:00
Nikolai Giman 4705055062 chore: update node to version 24 2026-01-04 22:03:03 +01:00
Nikolai Giman 7426f72257 chore: update build scripts and package configurations 2026-01-04 16:54:08 +01:00
Gimanh 64ddf4acfa Merge pull request #2 from Gimanh/chore/license-file-name
chore: update md
2026-01-03 20:57:30 +01:00
Nikolai Giman d843008091 chore: update md 2026-01-03 20:56:44 +01:00
Gimanh 95cbd330b1 Merge pull request #1 from Gimanh/chore/license-file-name
chore: license & cla
2026-01-03 17:34:07 +01:00
Nikolai Giman cf7055ce60 chore: license & cla 2026-01-03 17:24:57 +01:00
24 changed files with 714 additions and 1497 deletions
+2 -2
View File
@@ -28,11 +28,11 @@ jobs:
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN_CLA }}
with:
path-to-signatures: 'signatures/cla.json'
path-to-document: 'https://github.com/Gimanh/test-cla1/blob/main/CLA/CLA.md'
path-to-document: 'https://github.com/Gimanh/taskview-community/blob/main/CLA/CLA.md'
branch: 'cla-signatures'
allowlist: bot*,dependabot[bot]
custom-notsigned-prcomment: >
Thank you for your contribution!
Please read the CLA: $pathToCLADocument
Please read the CLA: https://github.com/Gimanh/taskview-community/blob/main/CLA/CLA.md
Then comment exactly:
"I have read the CLA Document and I hereby sign the CLA"
+2 -1
View File
@@ -35,4 +35,5 @@ tsconfig.app.tsbuildinfo
customer-license
!build-dockers.sh
!build-docker-api.sh
!build-docker-web.sh
!build-docker-web.sh
!entrypoint.sh
+1
View File
@@ -0,0 +1 @@
engine-strict=true
+7 -2
View File
@@ -1,6 +1,6 @@
# TaskView Source-Available License
Version 1.0 Draft (tailored for TaskView)
Version 1.0
---
@@ -113,9 +113,14 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMA
For commercial licensing inquiries, contact:
**[licensing@taskview.tech](mailto:licensing@taskview.tech)**
**[support@taskview.tech](mailto:support@taskview.tech)**
## 10. Termination
- Any violation of Sections 4 or 5 automatically terminates the rights granted under this License.
- Rights may be reinstated if You cure the violation within 21 days and cease all prohibited use.
---
Copyright (c) 2026 Nikolai Giman
All rights reserved.
+6 -2
View File
@@ -127,7 +127,7 @@ docker-compose up
Make sure the image versions match the version defined in the root package.json.
## Roadmap
- Migrate api-server to BunJS
- Plugin / extension system
- Migrate to NuxtUI or similar ui library
- Enterprise SSO and identity integrations
@@ -137,4 +137,8 @@ Make sure the image versions match the version defined in the root package.json.
Note for contributors: contributions are accepted under the CLA (see CONTRIBUTING.md). The Project is distributed under the TaskView Source-Available License.
© TaskView
---
TaskView is developed and maintained by Nikolai Giman.
Copyright © 2026 Nikolai Giman
+15 -10
View File
@@ -5,14 +5,16 @@
VERSION=$1
if [ -z "$VERSION" ]; then
echo "Usage: ./build-docker-api.sh <version>"
echo "Example: ./build-docker-api.sh 1.17.0"
exit 1
fi
node -v
# Build the server scripts for the docker container (obfuscated scripts)
npm run vite-build-docker
# Create the taskview-server.js file for running the server
# (taskview-server-api.jsc is bytecode file that will be created by bytenode when docker file is run)
echo "const bytenode = require('bytenode'); require('./taskview-server-api.jsc');" > ./dist/taskview-server.js
# Build the server scripts for the docker container
npm run build:docker
# Copy the production package.json to the dist folder
cp ./production.package.json ./dist/package.json
@@ -20,14 +22,17 @@ cp ./production.package.json ./dist/package.json
# Copy the production ecosystem.config.js to the dist folder
cp ./production.ecosystem.config.js ./dist/ecosystem.config.js
# Build the migration scripts for the docker container (obfuscated scripts)
npm run vite-build-migration
mkdir ./dist-migration/taskview
# Build the migration scripts for the docker container
npm run build:migration
mkdir -p ./dist-migration/taskview
cp -R ./src/migrations/taskview/* ./dist-migration/taskview
node ./commands/copy-migration-files.js
echo "Building docker image..."
docker buildx build --platform=linux/amd64,linux/arm64 -t gimanhead/taskview-ce-api-server:$VERSION -t gimanhead/taskview-ce-api-server:latest . --load
cd postgresql
bash ./build-docker-migrations.sh $VERSION
cd ..
cd ..
echo "Build complete! Image: gimanhead/taskview-ce-api-server:$VERSION"
+3 -3
View File
@@ -14,7 +14,7 @@ services:
timeout: 5s
retries: 5
migration:
image: gimanhead/taskview-ce-db-migration:1.18.0
image: gimanhead/taskview-ce-db-migration:1.18.2
restart: "no"
depends_on:
db:
@@ -23,12 +23,12 @@ services:
- ./.env.taskview
taskview-ce-webapp:
image: gimanhead/taskview-ce-webapp:1.18.0
image: gimanhead/taskview-ce-webapp:1.18.2
restart: "no"
ports:
- "8888:80"
taskview-api-server:
image: gimanhead/taskview-ce-api-server:1.18.0
image: gimanhead/taskview-ce-api-server:1.18.2
restart: "no"
ports:
- "1725:1401"
+3 -10
View File
@@ -1,10 +1,6 @@
FROM node:20-bullseye-slim
FROM node:24.12.0-alpine
RUN apt-get update && apt-get install -y \
curl \
build-essential \
qemu-user-static \
&& rm -rf /var/lib/apt/lists/*
RUN apk add --no-cache curl
RUN npm install pm2 -g
@@ -12,10 +8,7 @@ WORKDIR /usr/src/app
COPY ./dist .
RUN npm install
RUN npm run byte
RUN rm ./taskview-server-api.js
RUN rm ./taskview-server-docker.js
RUN npm install --omit=dev
EXPOSE 1401
+9 -12
View File
@@ -2,14 +2,12 @@
"name": "taskview-ce-api-server",
"version": "1.17.0",
"scripts": {
"byte": "bytenode --compile ./dist/taskview-server-api.js",
"vite-build": "vite build && javascript-obfuscator ./dist/taskview-server.js --output ./dist/taskview-server-api.js && npm run byte",
"vite-build-docker": "vite build --config ./vite.config.docker.mts && javascript-obfuscator ./dist/taskview-server-docker.js --output ./dist/taskview-server-api.js",
"vite-build-migration": "vite build --config ./vite.config-migration.mts",
"start": "bun run --watch ./server.ts",
"build": "bun build ./server.ts --outdir=dist --target=node",
"build-all": "bun build ./server.ts --outdir=dist --target=node --minify && javascript-obfuscator ./dist/server.js --output ./dist/taskview.js",
"start-prod": "NODE_ENV=production bun run --watch ./server.ts",
"dev": "bun run --watch ./server.ts",
"start": "NODE_ENV=production node ./dist/taskview-server.js",
"build": "vite build",
"build:docker": "vite build --config ./vite.config.docker.mts",
"build:migration": "vite build --config ./vite.config-migration.mts",
"build:all": "pnpm run build:docker && pnpm run build:migration",
"test": "vitest",
"type-check": "tsc --noEmit",
"lint": "biome lint .",
@@ -21,7 +19,6 @@
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-node-resolve": "^15.3.0",
"@types/bcrypt": "^5.0.2",
"@types/bun": "latest",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.7",
@@ -29,10 +26,7 @@
"@types/pg": "^8.15.5",
"@types/semver": "^7.5.8",
"aws-sdk": "^2.1691.0",
"bytenode": "1.5.6",
"javascript-obfuscator": "^4.1.1",
"mock-aws-s3": "^4.0.2",
"nexe": "^4.0.0-rc.6",
"nock": "^13.5.5",
"vite": "^5.4.9",
"vite-plugin-node": "^4.0.0",
@@ -63,5 +57,8 @@
"taskview-db-schemas": "workspace:^",
"terser": "^5.36.0",
"zod": "^3.23.8"
},
"engines": {
"node": ">=24 <25"
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
FROM node:20-slim
RUN apt-get update && apt-get install -y postgresql-client && apt-get clean
FROM node:24.12.0-alpine
RUN apk add --no-cache postgresql-client
# ENV DB_HOST=localhost
# ENV DB_USER=tvdbuser
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
# entrypoint.sh
echo "Waiting for the database service to be ready..."
# Wait for the database service to be ready
until PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c '\q'; do
>&2 echo "Postgres is unavailable - sleeping"
sleep 2
done
>&2 echo "Postgres is up - executing command"
# Check if tables exist in the 'tasks' schema using SQL query.
# 'count(*)' will return 0 if there are no tables.
# We read this result and make a decision.
TABLE_COUNT=$(PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'tasks';" | xargs)
if [ "$TABLE_COUNT" -eq 0 ]; then
echo "No tables found in the 'tasks' schema. Running migration with --create."
node /app-migration/taskview-db-migration.js --create
else
echo "Tables already exist in the 'tasks' schema ($TABLE_COUNT tables found). Running standard migration."
node /app-migration/taskview-db-migration.js
fi
# Check the exit code of the migration script
if [ $? -ne 0 ]; then
>&2 echo "Migration failed! Exiting with an error."
exit 1
else
>&2 echo "Migration successful!"
exit 0
fi
+3 -4
View File
@@ -1,9 +1,8 @@
{
"name": "taskview-ce-api-server",
"version": "1.17.0",
"scripts": {
"byte": "bytenode --compile ./taskview-server-api.js",
"start": "pm2-runtime start ecosystem.config.js --env production"
},
"dependencies": {
"bytenode": "1.5.6"
}
"dependencies": {}
}
+12
View File
@@ -282,5 +282,17 @@
"description": [
"Release 1.16.0"
]
},
"22": {
"version": "1.18.2",
"name": "Release 1.18.2",
"releaseDate": "20260108",
"scripts": [
"/1.18.2/0.1.18.2.sql",
"/1.18.2/all-triggers.sql"
],
"description": [
"Release 1.18.2"
]
}
}
@@ -0,0 +1,41 @@
--Delete orphaned records from history.tasks_tasks table
delete from history.tasks_tasks h
where not exists (
select 1
from tasks.tasks t
where t.id = h.task_id
);
--Delete orphaned records from history.tasks_goal_lists table
delete from history.tasks_goal_lists h
where not exists (
select 1
from tasks.goal_lists gl
where gl.id = h.goal_list_id
);
--Delete orphaned records from history.tasks_goals table
delete from history.tasks_goals h
where not exists (
select 1
from tasks.goals g
where g.id = h.goal_id
);
alter table history.tasks_tasks
add constraint fk_history_tasks_tasks_task_id
foreign key (task_id)
references tasks.tasks (id) on delete cascade;
alter table history.tasks_goal_lists
add constraint fk_history_tasks_goal_lists_goal_list_id
foreign key (goal_list_id)
references tasks.goal_lists (id) on delete cascade;
alter table history.tasks_goals
add constraint fk_history_tasks_goals_goal_id
foreign key (goal_id)
references tasks.goals (id) on delete cascade;
@@ -0,0 +1,557 @@
--1.
--Trigger set previous version
create or replace function app.trigger_set_previous_version()
returns trigger as
$date_complete$
begin
new.prev_version = old.version;
return new;
end;
$date_complete$
language plpgsql;
drop trigger if exists trigger_set_previous_version on app.version;
create trigger trigger_set_previous_version
before insert
on app.version
for each row
execute procedure app.trigger_set_previous_version();
--2.
--Trigger for adding owner for taskList from goal
create or replace function tasks.trigger_set_owner_for_component()
returns trigger as
$date_complete$
begin
new.owner = (select owner from tasks.goals where id = new.goal_id);
return new;
end;
$date_complete$
language plpgsql;
drop trigger if exists trigger_set_owner_for_component on tasks.goal_lists;
create trigger trigger_set_owner_for_component
before insert
on tasks.goal_lists
for each row
execute procedure tasks.trigger_set_owner_for_component();
--3.
--Trigger for updating date_complete for task
create or replace function tasks.update_date_complete()
returns trigger as
$date_complete$
begin
if new.complete != old.complete
then
if new.complete = true
then
update tasks.tasks set date_complete = now() where id = old.id;
else
update tasks.tasks set date_complete = null where id = old.id;
end if;
end if;
return new;
end;
$date_complete$
language plpgsql;
drop trigger if exists tr_update_date_complete on tasks.tasks;
create trigger tr_update_date_complete
after update
on tasks.tasks
for each row
execute procedure tasks.update_date_complete();
--4.
-- Delete user from collaboration if not assigned to any goal
create or replace function collaboration.delete_user_if_not_assigned_to_goal()
returns trigger as $$
declare
count int;
begin
if not exists (
select 1
from collaboration.users_to_goals
where user_id = old.user_id
limit 1
) then
delete from collaboration.users where id = old.user_id;
end if;
return old;
end;
$$ language plpgsql;
drop trigger if exists trigger_delete_user_if_not_assigned_to_goal on collaboration.users_to_goals;
create trigger trigger_delete_user_if_not_assigned_to_goal
after delete
on collaboration.users_to_goals
for each row
execute function collaboration.delete_user_if_not_assigned_to_goal();
--5.
--Trigger for checking task graph relation goal to avoid connection between tasks from different goals
create or replace function tasks.check_task_graph_relation_goal()
returns trigger as $$
declare
from_goal int;
to_goal int;
begin
select goal_id into from_goal from tasks.tasks where id = new.from_task_id;
select goal_id into to_goal from tasks.tasks where id = new.to_task_id;
if from_goal is null or to_goal is null then
raise exception 'Invalid task reference in relation';
end if;
if from_goal <> to_goal then
raise exception 'Relation goal_id must match both tasks'' goal_id';
end if;
new.goal_id := from_goal;
return new;
end;
$$ language plpgsql;
drop trigger if exists trigger_task_relation_goal on tasks.task_relations;
create trigger trigger_task_relation_goal
before insert or update on tasks.task_relations
for each row execute function tasks.check_task_graph_relation_goal();
--6.
--Trigger for logging changes in taskList to history table
create or replace function tasks.log_changes_tasks_goal_lists()
returns trigger as
$body$
begin
if tg_op = 'DELETE' then
insert into history.tasks_goal_lists (goal_list_id, edit_date, task, deleted) values (old.id, now(), to_jsonb(old), 1);
return old;
elseif tg_op = 'UPDATE' then
insert into history.tasks_goal_lists (goal_list_id, edit_date, task, deleted)
VALUES (old.id, new.date_creation, to_jsonb(old), 0);
new.edit_date = now();
return new;
end if;
end
$body$
language plpgsql;
drop trigger if exists trigger_log_changes_tasks_goal_lists on tasks.goal_lists;
create trigger trigger_log_changes_tasks_goal_lists
before update or delete
on tasks.goal_lists
for each row
execute procedure tasks.log_changes_tasks_goal_lists();
--7.
--Trigger for logging changes in goal to history table
create or replace function tasks.log_changes_tasks_goals()
returns trigger as
$body$
begin
if tg_op = 'DELETE' then
insert into history.tasks_goals (goal_id, edit_date, task, deleted) values (old.id, now(), to_jsonb(old), 1);
return old;
elseif tg_op = 'UPDATE' then
insert into history.tasks_goals (goal_id, edit_date, task, deleted)
VALUES (old.id, new.date_creation, to_jsonb(old), 0);
new.edit_date = now();
return new;
end if;
end
$body$
language plpgsql;
drop trigger if exists trigger_log_changes_tasks_goals on tasks.goals;
create trigger trigger_log_changes_tasks_goals
before update or delete
on tasks.goals
for each row
execute procedure tasks.log_changes_tasks_goals();
--8.
--Trigger for logging changes in task to history table
create or replace function tasks.log_changes_tasks_tasks()
returns trigger as
$body$
begin
if tg_op = 'DELETE' then
insert into history.tasks_tasks (task_id, edit_date, task, deleted) values (old.id, now(), to_jsonb(old), 1);
return old;
elseif tg_op = 'UPDATE' then
insert into history.tasks_tasks (task_id, edit_date, task, deleted)
VALUES (old.id, new.date_creation, to_jsonb(old), 0);
new.edit_date = now();
return new;
end if;
end
$body$
language plpgsql;
drop trigger if exists trigger_log_changes_tasks_tasks on tasks.tasks;
create trigger trigger_log_changes_tasks_tasks
before update or delete
on tasks.tasks
for each row
execute procedure tasks.log_changes_tasks_tasks();
--9.
--Trigger for setting goal_id default for task
CREATE OR REPLACE FUNCTION tasks.set_goal_id_default_for_task()
RETURNS TRIGGER AS
$$
DECLARE
goal_id INT;
BEGIN
SELECT gl.goal_id
INTO goal_id
FROM tasks.goal_lists gl
WHERE gl.id = NEW.goal_list_id;
IF goal_id IS NOT NULL THEN
NEW.goal_id := goal_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists before_insert_set_goal_id_for_task on tasks.tasks;
CREATE TRIGGER before_insert_set_goal_id_for_task
BEFORE INSERT OR UPDATE
ON tasks.tasks
FOR EACH ROW
EXECUTE FUNCTION tasks.set_goal_id_default_for_task();
--10.
--Trigger for adding default roles and permissions for goal
CREATE OR REPLACE FUNCTION tasks.add_roles_and_permissions()
RETURNS TRIGGER AS
$$
DECLARE
editor_role_id INTEGER;
executor_role_id INTEGER;
BEGIN
-- 1. Create role "editor"
INSERT INTO collaboration.roles (name, goal_id)
VALUES ('editor', NEW.id)
RETURNING id INTO editor_role_id;
-- 2. Create role "executor"
INSERT INTO collaboration.roles (name, goal_id)
VALUES ('executor', NEW.id)
RETURNING id INTO executor_role_id;
-- 3. Add permissions for role "editor"
INSERT INTO collaboration.permissions_to_role (role_id, permission_id)
SELECT editor_role_id, id
FROM tv_auth.permissions
WHERE name IN (
'goal_can_watch_content',
'goal_can_edit',
'goal_can_add_task_list',
'goal_can_manage_users',
'component_can_watch_content',
'component_can_edit',
'component_can_delete',
'component_can_add_tasks',
'task_can_edit_deadline',
'task_can_watch_subtasks',
'task_can_watch_note',
'task_can_recovery_history',
'task_can_watch_assigned_users',
'task_can_edit_priority',
'task_can_delete',
'task_can_watch_details',
'task_can_assign_users',
'task_can_add_subtasks',
'task_can_watch_tags',
'task_can_watch_priority',
'task_can_access_history',
'task_can_edit_tags',
'task_can_edit_description',
'task_can_edit_status',
'task_can_edit_note',
'kanban_can_manage',
'kanban_can_view',
'graph_can_manage',
'graph_can_view'
);
-- 4. Add permissions for role "viewver"
INSERT INTO collaboration.permissions_to_role (role_id, permission_id)
SELECT executor_role_id, id
FROM tv_auth.permissions
WHERE name IN (
'goal_can_watch_content',
'component_can_watch_content',
'component_can_add_tasks',
'task_can_watch_subtasks',
'task_can_watch_note',
'task_can_watch_assigned_users',
'task_can_watch_details',
'task_can_add_subtasks',
'task_can_watch_tags',
'task_can_watch_priority'
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists add_roles_after_insert on tasks.goals;
CREATE TRIGGER add_roles_after_insert
AFTER INSERT
ON tasks.goals
FOR EACH ROW
EXECUTE FUNCTION tasks.add_roles_and_permissions();
--11.
--Trigger for adjusting start and end dates for task
CREATE OR REPLACE FUNCTION tasks.adjust_start_and_end_dates()
RETURNS TRIGGER AS
$$
DECLARE
start_timestamp TIMESTAMPTZ;
end_timestamp TIMESTAMPTZ;
BEGIN
-- If start_date is NULL, then start_time should be NULL
IF NEW.start_date IS NULL THEN
NEW.start_time := NULL;
END IF;
-- If end_date is NULL, then end_time should be NULL
IF NEW.end_date IS NULL THEN
NEW.end_time := NULL;
END IF;
-- If both dates are set
IF NEW.start_date IS NOT NULL AND NEW.end_date IS NOT NULL THEN
-- Adjust dates
IF NEW.start_date > NEW.end_date THEN
-- If start_date is greater than end_date, set end_date to start_date
NEW.end_date := NEW.start_date;
-- end_time remains unchanged
ELSIF NEW.end_date < NEW.start_date THEN
-- If end_date is less than start_date, set start_date to end_date
NEW.start_date := NEW.end_date;
-- start_time remains unchanged
END IF;
-- Prepare timestamps for comparison
start_timestamp := (NEW.start_date::text || ' ' || COALESCE(NEW.start_time::text, '00:00:00+00'))::timestamptz;
end_timestamp := (NEW.end_date::text || ' ' || COALESCE(NEW.end_time::text, '00:00:00+00'))::timestamptz;
-- If start_timestamp is greater than end_timestamp, adjust end_date and end_time
IF start_timestamp > end_timestamp THEN
NEW.end_date := NEW.start_date;
-- Assign end_time only if start_time is not NULL
IF NEW.start_time IS NOT NULL AND NEW.end_time IS NOT NULL THEN
NEW.end_time := NEW.start_time;
END IF;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists adjust_dates_and_times_trigger on tasks.tasks;
CREATE TRIGGER adjust_dates_and_times_trigger
BEFORE INSERT OR UPDATE
ON tasks.tasks
FOR EACH ROW
EXECUTE FUNCTION tasks.adjust_start_and_end_dates();
--12.
--Trigger for adding self/owner to collaboration table to be able to assign tasks to self
create or replace function tasks.add_self_to_collaboration()
returns trigger as $$
DECLARE
owner_email TEXT;
BEGIN
select email into owner_email
from tv_auth.users
where id = NEW.owner;
if owner_email is not null then
insert into collaboration.users (email) values (owner_email) ON CONFLICT (email) DO NOTHING;
insert into collaboration.users_to_goals (goal_id, user_id) values (NEW.id, (select id from collaboration.users where email = owner_email));
end if;
return NEW;
END;
$$ language plpgsql;
drop trigger if exists add_selt_to_collaboration_trg on tasks.goals;
create trigger add_selt_to_collaboration_trg
after insert on tasks.goals
for each row
execute function tasks.add_self_to_collaboration();
--13.
--Trigger for adding default kanban columns for new goal
CREATE OR REPLACE FUNCTION tasks.kanban_add_default_columns()
RETURNS TRIGGER AS $$
BEGIN
-- Add default columns for new goal
INSERT INTO tasks.statuses (name, goal_id, view_order)
VALUES
('TODO', NEW.id, 1),
('In Progress', NEW.id, 2),
('Done', NEW.id, 3);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS kanban_add_default_columns_trg ON tasks.goals;
CREATE TRIGGER kanban_add_default_columns_trg
AFTER INSERT ON tasks.goals
FOR EACH ROW
EXECUTE FUNCTION tasks.kanban_add_default_columns();
--14.
--Trigger for validating the correct statusId for the inserted value. To avoid assigning a status that does not belong to the goal.
CREATE OR REPLACE FUNCTION tasks.check_task_status_goal()
RETURNS TRIGGER AS $$
BEGIN
-- Check if there is a record in tasks.statuses with the same goal_id
IF NOT EXISTS (
SELECT 1 FROM tasks.statuses s
WHERE s.id = NEW.status_id AND s.goal_id = NEW.goal_id
) THEN
RAISE EXCEPTION 'Status ID % is not valid for goal ID %', NEW.status_id, NEW.goal_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists enforce_task_status_goal on tasks.tasks;
CREATE TRIGGER enforce_task_status_goal
BEFORE INSERT OR UPDATE ON tasks.tasks
FOR EACH ROW
WHEN (NEW.status_id IS NOT NULL)
EXECUTE FUNCTION tasks.check_task_status_goal();
--15.
--Trigger for setting default orders value for task
CREATE OR REPLACE FUNCTION tasks.set_order_value()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.task_order IS NULL THEN
NEW.task_order := NEW.id;
END IF;
IF NEW.kanban_order IS NULL THEN
NEW.kanban_order := NEW.id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists set_order_trigger on tasks.tasks;
CREATE TRIGGER set_order_trigger
BEFORE INSERT ON tasks.tasks
FOR EACH ROW
EXECUTE FUNCTION tasks.set_order_value();
--16.
--Trigger for setting default view order for new status
CREATE OR REPLACE FUNCTION tasks.status_set_default_view_order()
RETURNS TRIGGER AS $$
DECLARE
new_view_order INT;
BEGIN
-- Determine the next view_order for the given goal_id
SELECT COALESCE(MAX(view_order), 0) + 1 INTO new_view_order
FROM tasks.statuses
WHERE goal_id = NEW.goal_id;
-- Assign the calculated value to the view_order field
NEW.view_order := new_view_order;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists set_default_status_view_order on tasks.statuses;
CREATE TRIGGER set_default_status_view_order
BEFORE INSERT ON tasks.statuses
FOR EACH ROW
EXECUTE FUNCTION tasks.status_set_default_view_order();
--17.
--Trigger for validating the correct user_id for the inserted value. To avoid assigning a user that does not belong to the goal.
CREATE OR REPLACE FUNCTION tasks_auth.control_user_id_is_from_same_goal_as_task()
RETURNS TRIGGER AS $$
DECLARE
user_exists BOOLEAN;
BEGIN
SELECT EXISTS (
SELECT 1
FROM tasks.tasks tt
LEFT JOIN collaboration.users_to_goals utg ON utg.goal_id = tt.goal_id
WHERE tt.id = NEW.task_id AND utg.user_id = NEW.collab_user_id
) INTO user_exists;
IF NOT user_exists THEN
RAISE EXCEPTION 'User % is not associated with the goal of task %', NEW.collab_user_id, NEW.task_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
drop trigger if exists trigger_control_user_id_is_from_same_goal_as_task on tasks_auth.task_assignee;
CREATE TRIGGER trigger_control_user_id_is_from_same_goal_as_task
BEFORE INSERT ON tasks_auth.task_assignee
FOR EACH ROW
EXECUTE FUNCTION tasks_auth.control_user_id_is_from_same_goal_as_task();
--18.
--Trigger for adding owner for task, extend owner from goal or taskList
--delete old function with wrong name
drop trigger if exists trigger_set_owner_for_task on tasks.tasks;
drop function if exists tasks.trigger_set_owner_for_task();
CREATE OR REPLACE FUNCTION tasks.fn_set_owner_for_task()
RETURNS TRIGGER AS
$body$
BEGIN
NEW.owner := COALESCE(
(SELECT owner FROM tasks.goal_lists WHERE id = NEW.goal_list_id),
(SELECT owner FROM tasks.goals WHERE id = NEW.goal_id)
);
IF NEW.owner IS NULL THEN
RAISE EXCEPTION 'Can not insert task without owner';
END IF;
RETURN NEW;
END;
$body$
LANGUAGE plpgsql;
drop trigger if exists trigger_set_owner_for_task on tasks.tasks;
create trigger trigger_set_owner_for_task
before insert
on tasks.tasks
for each row
execute procedure tasks.fn_set_owner_for_task();
+1 -1
View File
@@ -652,7 +652,7 @@ export class TasksRepository {
}
const result = await callWithCatch(() =>
this.db.dbDrizzle.select().from(TasksSchema).where(and(...conditions)).orderBy(asc(TasksSchema.kanbanOrder)).limit(10)
this.db.dbDrizzle.select().from(TasksSchema).where(and(...conditions)).orderBy(asc(TasksSchema.kanbanOrder)).limit(20)
);
return result ?? [];
}
+4 -4
View File
@@ -27,7 +27,7 @@ export default defineConfig({
ecma: 2015,
compress: true,
mangle: {
properties: false,//couse error if true after build
properties: false,//cause error if true after build
},
format: {
comments: true,
@@ -37,9 +37,9 @@ export default defineConfig({
external: ['pg-native'],
output: {
format: 'umd',
entryFileNames: 'taskview-server-docker.js',
chunkFileNames: 'taskview-server-docker-[hash].js',
assetFileNames: 'taskview-server-docker-[name]-[hash].[ext]',
entryFileNames: 'taskview-server.js',
chunkFileNames: 'taskview-server-[hash].js',
assetFileNames: 'taskview-server-[name]-[hash].[ext]',
},
},
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "taskview-ce-monorepo",
"version": "1.18.0",
"version": "1.18.2",
"private": true,
"description": "TaskView CE monorepo containing web, API, and packages",
"workspaces": [
+1 -1440
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "taskview-ce-api",
"name": "taskview-api",
"private": false,
"version": "1.17.0",
"type": "module",
@@ -1,5 +1,5 @@
{
"name": "taskview-ce-db-schemas",
"name": "taskview-db-schemas",
"version": "1.17.0",
"type": "module",
"main": "./dist/taskview-db-schemas.umd.js",
+3
View File
@@ -91,5 +91,8 @@
"vue-i18n": "11.1.10",
"vue-tsc": "3.0.3",
"vuetify": "3.7.9"
},
"engines": {
"node": ">=24 <25"
}
}
+1 -1
View File
@@ -202,7 +202,7 @@ const loadMoreTasks = (columnId: number) => {
console.log('loadMoreTasks', columnId);
kanbanStore.fetchTasksForColumn(
kanbanStore.goalId,
columnId,
columnId === DEFAULT_ID ? null : columnId,
kanbanStore.tasksData[columnId]?.tasks[kanbanStore.tasksData[columnId]?.tasks.length - 1]?.kanbanOrder || null
);
canShowLoadMore.value[columnId] = false;
+4
View File
@@ -115,6 +115,10 @@ export const useTasksStore = defineStore('tasks', {
if (mainTask) {
mainTask.subtasks.push(task);
}
if (this.selectedTask?.id === task.parentId) {
this.selectedTask.subtasks.push(task);
}
} else {
this.tasks.unshift(task);
}