mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 18:33:01 +00:00
issue types (task/bug)
This commit is contained in:
1
packages/backend/drizzle/0023_loving_raza.sql
Normal file
1
packages/backend/drizzle/0023_loving_raza.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "Organisation" ADD COLUMN "issueTypes" json DEFAULT '{"Task":{"icon":"checkBox","color":"#e4bd47"},"Bug":{"icon":"bug","color":"#ef4444"}}'::json NOT NULL;
|
||||||
2
packages/backend/drizzle/0024_military_stryfe.sql
Normal file
2
packages/backend/drizzle/0024_military_stryfe.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "Organisation" ALTER COLUMN "features" SET DEFAULT '{"userAvatars":true,"issueTypes":true,"issueStatus":true,"issueDescriptions":true,"issueTimeTracking":true,"issueAssignees":true,"issueAssigneesShownInTable":true,"issueCreator":true,"issueComments":true,"sprints":true}'::json;--> statement-breakpoint
|
||||||
|
ALTER TABLE "Issue" ADD COLUMN "type" varchar(16) DEFAULT 'Task' NOT NULL;
|
||||||
923
packages/backend/drizzle/meta/0023_snapshot.json
Normal file
923
packages/backend/drizzle/meta/0023_snapshot.json
Normal file
@@ -0,0 +1,923 @@
|
|||||||
|
{
|
||||||
|
"id": "c0d457d4-fbb8-412b-bc31-6647297996ee",
|
||||||
|
"prevId": "a147434f-8cb1-41e1-aec4-e745a5030012",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.Issue": {
|
||||||
|
"name": "Issue",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Issue_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projectId": {
|
||||||
|
"name": "projectId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"name": "number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(2048)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "varchar(24)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'TO DO'"
|
||||||
|
},
|
||||||
|
"creatorId": {
|
||||||
|
"name": "creatorId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"sprintId": {
|
||||||
|
"name": "sprintId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"unique_project_issue_number": {
|
||||||
|
"name": "unique_project_issue_number",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "projectId",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "number",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": true,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Issue_projectId_Project_id_fk": {
|
||||||
|
"name": "Issue_projectId_Project_id_fk",
|
||||||
|
"tableFrom": "Issue",
|
||||||
|
"tableTo": "Project",
|
||||||
|
"columnsFrom": [
|
||||||
|
"projectId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"Issue_creatorId_User_id_fk": {
|
||||||
|
"name": "Issue_creatorId_User_id_fk",
|
||||||
|
"tableFrom": "Issue",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"creatorId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"Issue_sprintId_Sprint_id_fk": {
|
||||||
|
"name": "Issue_sprintId_Sprint_id_fk",
|
||||||
|
"tableFrom": "Issue",
|
||||||
|
"tableTo": "Sprint",
|
||||||
|
"columnsFrom": [
|
||||||
|
"sprintId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.IssueAssignee": {
|
||||||
|
"name": "IssueAssignee",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "IssueAssignee_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issueId": {
|
||||||
|
"name": "issueId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"assignedAt": {
|
||||||
|
"name": "assignedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"unique_issue_user": {
|
||||||
|
"name": "unique_issue_user",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "issueId",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "userId",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": true,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"IssueAssignee_issueId_Issue_id_fk": {
|
||||||
|
"name": "IssueAssignee_issueId_Issue_id_fk",
|
||||||
|
"tableFrom": "IssueAssignee",
|
||||||
|
"tableTo": "Issue",
|
||||||
|
"columnsFrom": [
|
||||||
|
"issueId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"IssueAssignee_userId_User_id_fk": {
|
||||||
|
"name": "IssueAssignee_userId_User_id_fk",
|
||||||
|
"tableFrom": "IssueAssignee",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.IssueComment": {
|
||||||
|
"name": "IssueComment",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "IssueComment_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issueId": {
|
||||||
|
"name": "issueId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"name": "body",
|
||||||
|
"type": "varchar(2048)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"IssueComment_issueId_Issue_id_fk": {
|
||||||
|
"name": "IssueComment_issueId_Issue_id_fk",
|
||||||
|
"tableFrom": "IssueComment",
|
||||||
|
"tableTo": "Issue",
|
||||||
|
"columnsFrom": [
|
||||||
|
"issueId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"IssueComment_userId_User_id_fk": {
|
||||||
|
"name": "IssueComment_userId_User_id_fk",
|
||||||
|
"tableFrom": "IssueComment",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Organisation": {
|
||||||
|
"name": "Organisation",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Organisation_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(1024)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"iconURL": {
|
||||||
|
"name": "iconURL",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"name": "statuses",
|
||||||
|
"type": "json",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{\"TO DO\":\"#fafafa\",\"IN PROGRESS\":\"#f97316\",\"REVIEW\":\"#8952bc\",\"DONE\":\"#22c55e\",\"REJECTED\":\"#ef4444\",\"ARCHIVED\":\"#a1a1a1\",\"MERGED\":\"#a1a1a1\"}'::json"
|
||||||
|
},
|
||||||
|
"issueTypes": {
|
||||||
|
"name": "issueTypes",
|
||||||
|
"type": "json",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{\"Task\":{\"icon\":\"checkBox\",\"color\":\"#e4bd47\"},\"Bug\":{\"icon\":\"bug\",\"color\":\"#ef4444\"}}'::json"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"name": "features",
|
||||||
|
"type": "json",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{\"userAvatars\":true,\"issueStatus\":true,\"issueDescriptions\":true,\"issueTimeTracking\":true,\"issueAssignees\":true,\"issueAssigneesShownInTable\":true,\"issueCreator\":true,\"issueComments\":true,\"sprints\":true}'::json"
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"Organisation_slug_unique": {
|
||||||
|
"name": "Organisation_slug_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"slug"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.OrganisationMember": {
|
||||||
|
"name": "OrganisationMember",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "OrganisationMember_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organisationId": {
|
||||||
|
"name": "organisationId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"OrganisationMember_organisationId_Organisation_id_fk": {
|
||||||
|
"name": "OrganisationMember_organisationId_Organisation_id_fk",
|
||||||
|
"tableFrom": "OrganisationMember",
|
||||||
|
"tableTo": "Organisation",
|
||||||
|
"columnsFrom": [
|
||||||
|
"organisationId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"OrganisationMember_userId_User_id_fk": {
|
||||||
|
"name": "OrganisationMember_userId_User_id_fk",
|
||||||
|
"tableFrom": "OrganisationMember",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Project": {
|
||||||
|
"name": "Project",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Project_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "varchar(4)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"organisationId": {
|
||||||
|
"name": "organisationId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"creatorId": {
|
||||||
|
"name": "creatorId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Project_organisationId_Organisation_id_fk": {
|
||||||
|
"name": "Project_organisationId_Organisation_id_fk",
|
||||||
|
"tableFrom": "Project",
|
||||||
|
"tableTo": "Organisation",
|
||||||
|
"columnsFrom": [
|
||||||
|
"organisationId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"Project_creatorId_User_id_fk": {
|
||||||
|
"name": "Project_creatorId_User_id_fk",
|
||||||
|
"tableFrom": "Project",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"creatorId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Session": {
|
||||||
|
"name": "Session",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Session_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"csrfToken": {
|
||||||
|
"name": "csrfToken",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expiresAt": {
|
||||||
|
"name": "expiresAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Session_userId_User_id_fk": {
|
||||||
|
"name": "Session_userId_User_id_fk",
|
||||||
|
"tableFrom": "Session",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Sprint": {
|
||||||
|
"name": "Sprint",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Sprint_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projectId": {
|
||||||
|
"name": "projectId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"name": "color",
|
||||||
|
"type": "varchar(7)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'#a1a1a1'"
|
||||||
|
},
|
||||||
|
"startDate": {
|
||||||
|
"name": "startDate",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"endDate": {
|
||||||
|
"name": "endDate",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Sprint_projectId_Project_id_fk": {
|
||||||
|
"name": "Sprint_projectId_Project_id_fk",
|
||||||
|
"tableFrom": "Sprint",
|
||||||
|
"tableTo": "Project",
|
||||||
|
"columnsFrom": [
|
||||||
|
"projectId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.TimedSession": {
|
||||||
|
"name": "TimedSession",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "TimedSession_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"issueId": {
|
||||||
|
"name": "issueId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"timestamps": {
|
||||||
|
"name": "timestamps",
|
||||||
|
"type": "timestamp[]",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"endedAt": {
|
||||||
|
"name": "endedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"TimedSession_userId_User_id_fk": {
|
||||||
|
"name": "TimedSession_userId_User_id_fk",
|
||||||
|
"tableFrom": "TimedSession",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"TimedSession_issueId_Issue_id_fk": {
|
||||||
|
"name": "TimedSession_issueId_Issue_id_fk",
|
||||||
|
"tableFrom": "TimedSession",
|
||||||
|
"tableTo": "Issue",
|
||||||
|
"columnsFrom": [
|
||||||
|
"issueId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.User": {
|
||||||
|
"name": "User",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "User_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"passwordHash": {
|
||||||
|
"name": "passwordHash",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"avatarURL": {
|
||||||
|
"name": "avatarURL",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"iconPreference": {
|
||||||
|
"name": "iconPreference",
|
||||||
|
"type": "varchar(10)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'lucide'"
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"User_username_unique": {
|
||||||
|
"name": "User_username_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
930
packages/backend/drizzle/meta/0024_snapshot.json
Normal file
930
packages/backend/drizzle/meta/0024_snapshot.json
Normal file
@@ -0,0 +1,930 @@
|
|||||||
|
{
|
||||||
|
"id": "95c0fe99-9c32-4baf-a635-3be4c92b90de",
|
||||||
|
"prevId": "c0d457d4-fbb8-412b-bc31-6647297996ee",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.Issue": {
|
||||||
|
"name": "Issue",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Issue_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projectId": {
|
||||||
|
"name": "projectId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"name": "number",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "varchar(16)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'Task'"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "varchar(24)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'TO DO'"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(2048)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"creatorId": {
|
||||||
|
"name": "creatorId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"sprintId": {
|
||||||
|
"name": "sprintId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"unique_project_issue_number": {
|
||||||
|
"name": "unique_project_issue_number",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "projectId",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "number",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": true,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Issue_projectId_Project_id_fk": {
|
||||||
|
"name": "Issue_projectId_Project_id_fk",
|
||||||
|
"tableFrom": "Issue",
|
||||||
|
"tableTo": "Project",
|
||||||
|
"columnsFrom": [
|
||||||
|
"projectId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"Issue_creatorId_User_id_fk": {
|
||||||
|
"name": "Issue_creatorId_User_id_fk",
|
||||||
|
"tableFrom": "Issue",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"creatorId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"Issue_sprintId_Sprint_id_fk": {
|
||||||
|
"name": "Issue_sprintId_Sprint_id_fk",
|
||||||
|
"tableFrom": "Issue",
|
||||||
|
"tableTo": "Sprint",
|
||||||
|
"columnsFrom": [
|
||||||
|
"sprintId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.IssueAssignee": {
|
||||||
|
"name": "IssueAssignee",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "IssueAssignee_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issueId": {
|
||||||
|
"name": "issueId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"assignedAt": {
|
||||||
|
"name": "assignedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"unique_issue_user": {
|
||||||
|
"name": "unique_issue_user",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "issueId",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expression": "userId",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": true,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"IssueAssignee_issueId_Issue_id_fk": {
|
||||||
|
"name": "IssueAssignee_issueId_Issue_id_fk",
|
||||||
|
"tableFrom": "IssueAssignee",
|
||||||
|
"tableTo": "Issue",
|
||||||
|
"columnsFrom": [
|
||||||
|
"issueId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"IssueAssignee_userId_User_id_fk": {
|
||||||
|
"name": "IssueAssignee_userId_User_id_fk",
|
||||||
|
"tableFrom": "IssueAssignee",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.IssueComment": {
|
||||||
|
"name": "IssueComment",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "IssueComment_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"issueId": {
|
||||||
|
"name": "issueId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"name": "body",
|
||||||
|
"type": "varchar(2048)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"IssueComment_issueId_Issue_id_fk": {
|
||||||
|
"name": "IssueComment_issueId_Issue_id_fk",
|
||||||
|
"tableFrom": "IssueComment",
|
||||||
|
"tableTo": "Issue",
|
||||||
|
"columnsFrom": [
|
||||||
|
"issueId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"IssueComment_userId_User_id_fk": {
|
||||||
|
"name": "IssueComment_userId_User_id_fk",
|
||||||
|
"tableFrom": "IssueComment",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Organisation": {
|
||||||
|
"name": "Organisation",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Organisation_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(1024)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"iconURL": {
|
||||||
|
"name": "iconURL",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"name": "statuses",
|
||||||
|
"type": "json",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{\"TO DO\":\"#fafafa\",\"IN PROGRESS\":\"#f97316\",\"REVIEW\":\"#8952bc\",\"DONE\":\"#22c55e\",\"REJECTED\":\"#ef4444\",\"ARCHIVED\":\"#a1a1a1\",\"MERGED\":\"#a1a1a1\"}'::json"
|
||||||
|
},
|
||||||
|
"issueTypes": {
|
||||||
|
"name": "issueTypes",
|
||||||
|
"type": "json",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{\"Task\":{\"icon\":\"checkBox\",\"color\":\"#e4bd47\"},\"Bug\":{\"icon\":\"bug\",\"color\":\"#ef4444\"}}'::json"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"name": "features",
|
||||||
|
"type": "json",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{\"userAvatars\":true,\"issueTypes\":true,\"issueStatus\":true,\"issueDescriptions\":true,\"issueTimeTracking\":true,\"issueAssignees\":true,\"issueAssigneesShownInTable\":true,\"issueCreator\":true,\"issueComments\":true,\"sprints\":true}'::json"
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"Organisation_slug_unique": {
|
||||||
|
"name": "Organisation_slug_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"slug"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.OrganisationMember": {
|
||||||
|
"name": "OrganisationMember",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "OrganisationMember_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organisationId": {
|
||||||
|
"name": "organisationId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"OrganisationMember_organisationId_Organisation_id_fk": {
|
||||||
|
"name": "OrganisationMember_organisationId_Organisation_id_fk",
|
||||||
|
"tableFrom": "OrganisationMember",
|
||||||
|
"tableTo": "Organisation",
|
||||||
|
"columnsFrom": [
|
||||||
|
"organisationId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"OrganisationMember_userId_User_id_fk": {
|
||||||
|
"name": "OrganisationMember_userId_User_id_fk",
|
||||||
|
"tableFrom": "OrganisationMember",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Project": {
|
||||||
|
"name": "Project",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Project_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "varchar(4)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"organisationId": {
|
||||||
|
"name": "organisationId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"creatorId": {
|
||||||
|
"name": "creatorId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Project_organisationId_Organisation_id_fk": {
|
||||||
|
"name": "Project_organisationId_Organisation_id_fk",
|
||||||
|
"tableFrom": "Project",
|
||||||
|
"tableTo": "Organisation",
|
||||||
|
"columnsFrom": [
|
||||||
|
"organisationId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"Project_creatorId_User_id_fk": {
|
||||||
|
"name": "Project_creatorId_User_id_fk",
|
||||||
|
"tableFrom": "Project",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"creatorId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Session": {
|
||||||
|
"name": "Session",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Session_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"csrfToken": {
|
||||||
|
"name": "csrfToken",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expiresAt": {
|
||||||
|
"name": "expiresAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Session_userId_User_id_fk": {
|
||||||
|
"name": "Session_userId_User_id_fk",
|
||||||
|
"tableFrom": "Session",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.Sprint": {
|
||||||
|
"name": "Sprint",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "Sprint_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projectId": {
|
||||||
|
"name": "projectId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"name": "color",
|
||||||
|
"type": "varchar(7)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'#a1a1a1'"
|
||||||
|
},
|
||||||
|
"startDate": {
|
||||||
|
"name": "startDate",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"endDate": {
|
||||||
|
"name": "endDate",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"Sprint_projectId_Project_id_fk": {
|
||||||
|
"name": "Sprint_projectId_Project_id_fk",
|
||||||
|
"tableFrom": "Sprint",
|
||||||
|
"tableTo": "Project",
|
||||||
|
"columnsFrom": [
|
||||||
|
"projectId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.TimedSession": {
|
||||||
|
"name": "TimedSession",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "TimedSession_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"name": "userId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"issueId": {
|
||||||
|
"name": "issueId",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"timestamps": {
|
||||||
|
"name": "timestamps",
|
||||||
|
"type": "timestamp[]",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"endedAt": {
|
||||||
|
"name": "endedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"TimedSession_userId_User_id_fk": {
|
||||||
|
"name": "TimedSession_userId_User_id_fk",
|
||||||
|
"tableFrom": "TimedSession",
|
||||||
|
"tableTo": "User",
|
||||||
|
"columnsFrom": [
|
||||||
|
"userId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"TimedSession_issueId_Issue_id_fk": {
|
||||||
|
"name": "TimedSession_issueId_Issue_id_fk",
|
||||||
|
"tableFrom": "TimedSession",
|
||||||
|
"tableTo": "Issue",
|
||||||
|
"columnsFrom": [
|
||||||
|
"issueId"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "set null",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.User": {
|
||||||
|
"name": "User",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "User_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"passwordHash": {
|
||||||
|
"name": "passwordHash",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"avatarURL": {
|
||||||
|
"name": "avatarURL",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"iconPreference": {
|
||||||
|
"name": "iconPreference",
|
||||||
|
"type": "varchar(10)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'lucide'"
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"User_username_unique": {
|
||||||
|
"name": "User_username_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"username"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -162,6 +162,20 @@
|
|||||||
"when": 1769265175455,
|
"when": 1769265175455,
|
||||||
"tag": "0022_unique_johnny_blaze",
|
"tag": "0022_unique_johnny_blaze",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 23,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1769294969462,
|
||||||
|
"tag": "0023_loving_raza",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 24,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1769295524021,
|
||||||
|
"tag": "0024_military_stryfe",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,7 @@ export async function updateIssue(
|
|||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
sprintId?: number | null;
|
sprintId?: number | null;
|
||||||
|
type?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ export default async function issueUpdate(req: AuthedRequest) {
|
|||||||
const parsed = await parseJsonBody(req, IssueUpdateRequestSchema);
|
const parsed = await parseJsonBody(req, IssueUpdateRequestSchema);
|
||||||
if ("error" in parsed) return parsed.error;
|
if ("error" in parsed) return parsed.error;
|
||||||
|
|
||||||
const { id, title, description, status, assigneeIds, sprintId } = parsed.data;
|
const { id, title, description, type, status, assigneeIds, sprintId } = parsed.data;
|
||||||
|
|
||||||
// check that at least one field is being updated
|
// check that at least one field is being updated
|
||||||
if (
|
if (
|
||||||
title === undefined &&
|
title === undefined &&
|
||||||
description === undefined &&
|
description === undefined &&
|
||||||
|
type === undefined &&
|
||||||
status === undefined &&
|
status === undefined &&
|
||||||
assigneeIds === undefined &&
|
assigneeIds === undefined &&
|
||||||
sprintId === undefined
|
sprintId === undefined
|
||||||
@@ -27,7 +28,11 @@ export default async function issueUpdate(req: AuthedRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasIssueFieldUpdates =
|
const hasIssueFieldUpdates =
|
||||||
title !== undefined || description !== undefined || status !== undefined || sprintId !== undefined;
|
title !== undefined ||
|
||||||
|
description !== undefined ||
|
||||||
|
type !== undefined ||
|
||||||
|
status !== undefined ||
|
||||||
|
sprintId !== undefined;
|
||||||
|
|
||||||
const existingIssue = await getIssueByID(id);
|
const existingIssue = await getIssueByID(id);
|
||||||
if (!existingIssue) {
|
if (!existingIssue) {
|
||||||
@@ -53,6 +58,7 @@ export default async function issueUpdate(req: AuthedRequest) {
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
sprintId,
|
sprintId,
|
||||||
|
type,
|
||||||
status,
|
status,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export function IssueComments({ issueId, className }: { issueId: number; classNa
|
|||||||
disabled={deletingId === comment.Comment.id}
|
disabled={deletingId === comment.Comment.id}
|
||||||
title="Delete comment"
|
title="Delete comment"
|
||||||
>
|
>
|
||||||
<Icon icon="trash" className="size-4" />
|
<Icon icon="trash" color="var(--destructive)" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ import { StatusSelect } from "@/components/status-select";
|
|||||||
import StatusTag from "@/components/status-tag";
|
import StatusTag from "@/components/status-tag";
|
||||||
import { TimerDisplay } from "@/components/timer-display";
|
import { TimerDisplay } from "@/components/timer-display";
|
||||||
import { TimerModal } from "@/components/timer-modal";
|
import { TimerModal } from "@/components/timer-modal";
|
||||||
|
import { TypeSelect } from "@/components/type-select";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||||
import Icon from "@/components/ui/icon";
|
import Icon, { type IconName } from "@/components/ui/icon";
|
||||||
import { IconButton } from "@/components/ui/icon-button";
|
import { IconButton } from "@/components/ui/icon-button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { SelectTrigger } from "@/components/ui/select";
|
import { SelectTrigger } from "@/components/ui/select";
|
||||||
@@ -58,6 +59,7 @@ export function IssueDetails({
|
|||||||
const [assigneeIds, setAssigneeIds] = useState<string[]>([]);
|
const [assigneeIds, setAssigneeIds] = useState<string[]>([]);
|
||||||
const [sprintId, setSprintId] = useState<string>("unassigned");
|
const [sprintId, setSprintId] = useState<string>("unassigned");
|
||||||
const [status, setStatus] = useState<string>("");
|
const [status, setStatus] = useState<string>("");
|
||||||
|
const [type, setType] = useState<string>("");
|
||||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||||
const [linkCopied, setLinkCopied] = useState(false);
|
const [linkCopied, setLinkCopied] = useState(false);
|
||||||
const copyTimeoutRef = useRef<number | null>(null);
|
const copyTimeoutRef = useRef<number | null>(null);
|
||||||
@@ -72,6 +74,11 @@ export function IssueDetails({
|
|||||||
const [isSavingDescription, setIsSavingDescription] = useState(false);
|
const [isSavingDescription, setIsSavingDescription] = useState(false);
|
||||||
const descriptionRef = useRef<HTMLTextAreaElement>(null);
|
const descriptionRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
const issueTypes = (organisation?.Organisation.issueTypes ?? {}) as Record<
|
||||||
|
string,
|
||||||
|
{ icon: string; color: string }
|
||||||
|
>;
|
||||||
|
|
||||||
const isAssignee = assigneeIds.some((id) => user?.id === Number(id));
|
const isAssignee = assigneeIds.some((id) => user?.id === Number(id));
|
||||||
const actualAssigneeIds = assigneeIds.filter((id) => id !== "unassigned");
|
const actualAssigneeIds = assigneeIds.filter((id) => id !== "unassigned");
|
||||||
const hasMultipleAssignees = actualAssigneeIds.length > 1;
|
const hasMultipleAssignees = actualAssigneeIds.length > 1;
|
||||||
@@ -80,6 +87,7 @@ export function IssueDetails({
|
|||||||
setSprintId(issueData.Issue.sprintId?.toString() ?? "unassigned");
|
setSprintId(issueData.Issue.sprintId?.toString() ?? "unassigned");
|
||||||
setAssigneeIds(assigneesToStringArray(issueData.Assignees));
|
setAssigneeIds(assigneesToStringArray(issueData.Assignees));
|
||||||
setStatus(issueData.Issue.status);
|
setStatus(issueData.Issue.status);
|
||||||
|
setType(issueData.Issue.type);
|
||||||
setTitle(issueData.Issue.title);
|
setTitle(issueData.Issue.title);
|
||||||
setOriginalTitle(issueData.Issue.title);
|
setOriginalTitle(issueData.Issue.title);
|
||||||
setDescription(issueData.Issue.description);
|
setDescription(issueData.Issue.description);
|
||||||
@@ -206,6 +214,38 @@ export function IssueDetails({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTypeChange = async (value: string) => {
|
||||||
|
setType(value);
|
||||||
|
const typeConfig = issueTypes[value];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateIssue.mutateAsync({
|
||||||
|
id: issueData.Issue.id,
|
||||||
|
type: value,
|
||||||
|
});
|
||||||
|
toast.success(
|
||||||
|
<span className="inline-flex items-center gap-1.5">
|
||||||
|
{issueID(projectKey, issueData.Issue.number)}'s type updated to{" "}
|
||||||
|
{typeConfig ? (
|
||||||
|
<span className="inline-flex items-center gap-1.5">
|
||||||
|
<Icon icon={typeConfig.icon as IconName} size={16} color={typeConfig.color} />
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
value
|
||||||
|
)}
|
||||||
|
</span>,
|
||||||
|
{ dismissible: false },
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("error updating type:", error);
|
||||||
|
setType(issueData.Issue.type);
|
||||||
|
toast.error(`Error updating type: ${parseError(error as Error)}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
setDeleteOpen(true);
|
setDeleteOpen(true);
|
||||||
};
|
};
|
||||||
@@ -310,7 +350,7 @@ export function IssueDetails({
|
|||||||
{linkCopied ? <Icon icon="check" /> : <Icon icon="link" />}
|
{linkCopied ? <Icon icon="check" /> : <Icon icon="link" />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton variant="destructive" onClick={handleDelete} title={"Delete issue"}>
|
<IconButton variant="destructive" onClick={handleDelete} title={"Delete issue"}>
|
||||||
<Icon icon="trash" />
|
<Icon icon="trash" color="var(--destructive)" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton onClick={onClose} title={"Close"}>
|
<IconButton onClick={onClose} title={"Close"}>
|
||||||
<Icon icon="x" />
|
<Icon icon="x" />
|
||||||
@@ -321,6 +361,30 @@ export function IssueDetails({
|
|||||||
|
|
||||||
<div className="flex flex-col w-full p-2 py-2 gap-2 max-h-[75vh] overflow-y-scroll">
|
<div className="flex flex-col w-full p-2 py-2 gap-2 max-h-[75vh] overflow-y-scroll">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
|
{organisation?.Organisation.features.issueTypes && Object.keys(issueTypes).length > 0 && (
|
||||||
|
<TypeSelect
|
||||||
|
issueTypes={issueTypes}
|
||||||
|
value={type}
|
||||||
|
onChange={handleTypeChange}
|
||||||
|
trigger={({ isOpen, value }) => {
|
||||||
|
const typeConfig = issueTypes[value];
|
||||||
|
return (
|
||||||
|
<SelectTrigger
|
||||||
|
className="group w-auto flex items-center"
|
||||||
|
variant="unstyled"
|
||||||
|
chevronClassName="hidden"
|
||||||
|
isOpen={isOpen}
|
||||||
|
>
|
||||||
|
{typeConfig ? (
|
||||||
|
<Icon icon={typeConfig.icon as IconName} size={18} color={typeConfig.color} />
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">Type</span>
|
||||||
|
)}
|
||||||
|
</SelectTrigger>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{organisation?.Organisation.features.issueStatus && (
|
{organisation?.Organisation.features.issueStatus && (
|
||||||
<StatusSelect
|
<StatusSelect
|
||||||
statuses={statuses}
|
statuses={statuses}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useAuthenticatedSession } from "@/components/session-provider";
|
|||||||
import { SprintSelect } from "@/components/sprint-select";
|
import { SprintSelect } from "@/components/sprint-select";
|
||||||
import { StatusSelect } from "@/components/status-select";
|
import { StatusSelect } from "@/components/status-select";
|
||||||
import StatusTag from "@/components/status-tag";
|
import StatusTag from "@/components/status-tag";
|
||||||
|
import { TypeSelect } from "@/components/type-select";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -17,6 +18,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
|
import Icon, { type IconName } from "@/components/ui/icon";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { SelectTrigger } from "@/components/ui/select";
|
import { SelectTrigger } from "@/components/ui/select";
|
||||||
import {
|
import {
|
||||||
@@ -39,8 +41,14 @@ export function IssueForm({ trigger }: { trigger?: React.ReactNode }) {
|
|||||||
|
|
||||||
const members = useMemo(() => membersData.map((member) => member.User), [membersData]);
|
const members = useMemo(() => membersData.map((member) => member.User), [membersData]);
|
||||||
const statuses = selectedOrganisation?.Organisation.statuses ?? {};
|
const statuses = selectedOrganisation?.Organisation.statuses ?? {};
|
||||||
|
const issueTypes = (selectedOrganisation?.Organisation.issueTypes ?? {}) as Record<
|
||||||
|
string,
|
||||||
|
{ icon: string; color: string }
|
||||||
|
>;
|
||||||
const statusOptions = useMemo(() => Object.keys(statuses), [statuses]);
|
const statusOptions = useMemo(() => Object.keys(statuses), [statuses]);
|
||||||
|
const typeOptions = useMemo(() => Object.keys(issueTypes), [issueTypes]);
|
||||||
const defaultStatus = statusOptions[0] ?? "";
|
const defaultStatus = statusOptions[0] ?? "";
|
||||||
|
const defaultType = typeOptions[0] ?? "";
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
@@ -48,6 +56,7 @@ export function IssueForm({ trigger }: { trigger?: React.ReactNode }) {
|
|||||||
const [sprintId, setSprintId] = useState<string>("unassigned");
|
const [sprintId, setSprintId] = useState<string>("unassigned");
|
||||||
const [assigneeIds, setAssigneeIds] = useState<string[]>(["unassigned"]);
|
const [assigneeIds, setAssigneeIds] = useState<string[]>(["unassigned"]);
|
||||||
const [status, setStatus] = useState<string>(defaultStatus);
|
const [status, setStatus] = useState<string>(defaultStatus);
|
||||||
|
const [type, setType] = useState<string>(defaultType);
|
||||||
const [submitAttempted, setSubmitAttempted] = useState(false);
|
const [submitAttempted, setSubmitAttempted] = useState(false);
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -58,6 +67,7 @@ export function IssueForm({ trigger }: { trigger?: React.ReactNode }) {
|
|||||||
setSprintId("unassigned");
|
setSprintId("unassigned");
|
||||||
setAssigneeIds(["unassigned"]);
|
setAssigneeIds(["unassigned"]);
|
||||||
setStatus(defaultStatus);
|
setStatus(defaultStatus);
|
||||||
|
setType(defaultType);
|
||||||
setSubmitAttempted(false);
|
setSubmitAttempted(false);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -103,6 +113,7 @@ export function IssueForm({ trigger }: { trigger?: React.ReactNode }) {
|
|||||||
sprintId: sprintId === "unassigned" ? null : Number(sprintId),
|
sprintId: sprintId === "unassigned" ? null : Number(sprintId),
|
||||||
assigneeIds: assigneeIds.filter((id) => id !== "unassigned").map((id) => Number(id)),
|
assigneeIds: assigneeIds.filter((id) => id !== "unassigned").map((id) => Number(id)),
|
||||||
status: status.trim() === "" ? undefined : status,
|
status: status.trim() === "" ? undefined : status,
|
||||||
|
type: type.trim() === "" ? undefined : type,
|
||||||
});
|
});
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
reset();
|
reset();
|
||||||
@@ -136,27 +147,61 @@ export function IssueForm({ trigger }: { trigger?: React.ReactNode }) {
|
|||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="grid">
|
<div className="grid">
|
||||||
{statusOptions.length > 0 && (
|
{(typeOptions.length > 0 || statusOptions.length > 0) && (
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<div className="flex items-center gap-3 mb-4 flex-wrap">
|
||||||
<Label>Status</Label>
|
{selectedOrganisation?.Organisation.features.issueTypes && typeOptions.length > 0 && (
|
||||||
<StatusSelect
|
<div className="flex items-center gap-2">
|
||||||
statuses={statuses}
|
<Label>Type</Label>
|
||||||
value={status}
|
<TypeSelect
|
||||||
onChange={(newValue) => {
|
issueTypes={issueTypes}
|
||||||
if (newValue.trim() === "") return;
|
value={type}
|
||||||
setStatus(newValue);
|
onChange={(newValue) => {
|
||||||
}}
|
if (newValue.trim() === "") return;
|
||||||
trigger={({ isOpen, value }) => (
|
setType(newValue);
|
||||||
<SelectTrigger
|
}}
|
||||||
className="group flex items-center w-min"
|
trigger={({ isOpen, value }) => {
|
||||||
variant="unstyled"
|
const typeConfig = issueTypes[value];
|
||||||
chevronClassName="hidden"
|
return (
|
||||||
isOpen={isOpen}
|
<SelectTrigger
|
||||||
>
|
className="group flex items-center w-min"
|
||||||
<StatusTag status={value} colour={statuses[value]} className="hover:opacity-85" />
|
variant="unstyled"
|
||||||
</SelectTrigger>
|
chevronClassName="hidden"
|
||||||
)}
|
isOpen={isOpen}
|
||||||
/>
|
>
|
||||||
|
{typeConfig ? (
|
||||||
|
<Icon icon={typeConfig.icon as IconName} size={20} color={typeConfig.color} />
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">Type</span>
|
||||||
|
)}
|
||||||
|
</SelectTrigger>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{statusOptions.length > 0 && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label>Status</Label>
|
||||||
|
<StatusSelect
|
||||||
|
statuses={statuses}
|
||||||
|
value={status}
|
||||||
|
onChange={(newValue) => {
|
||||||
|
if (newValue.trim() === "") return;
|
||||||
|
setStatus(newValue);
|
||||||
|
}}
|
||||||
|
trigger={({ isOpen, value }) => (
|
||||||
|
<SelectTrigger
|
||||||
|
className="group flex items-center w-min"
|
||||||
|
variant="unstyled"
|
||||||
|
chevronClassName="hidden"
|
||||||
|
isOpen={isOpen}
|
||||||
|
>
|
||||||
|
<StatusTag status={value} colour={statuses[value]} className="hover:opacity-85" />
|
||||||
|
</SelectTrigger>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useMemo } from "react";
|
|||||||
import Avatar from "@/components/avatar";
|
import Avatar from "@/components/avatar";
|
||||||
import { useSelection } from "@/components/selection-provider";
|
import { useSelection } from "@/components/selection-provider";
|
||||||
import StatusTag from "@/components/status-tag";
|
import StatusTag from "@/components/status-tag";
|
||||||
|
import Icon, { type IconName } from "@/components/ui/icon";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { useIssues, useSelectedOrganisation, useSelectedProject } from "@/lib/query/hooks";
|
import { useIssues, useSelectedOrganisation, useSelectedProject } from "@/lib/query/hooks";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -18,6 +19,10 @@ export function IssuesTable({
|
|||||||
const selectedOrganisation = useSelectedOrganisation();
|
const selectedOrganisation = useSelectedOrganisation();
|
||||||
const selectedProject = useSelectedProject();
|
const selectedProject = useSelectedProject();
|
||||||
const statuses = selectedOrganisation?.Organisation.statuses ?? {};
|
const statuses = selectedOrganisation?.Organisation.statuses ?? {};
|
||||||
|
const issueTypes = (selectedOrganisation?.Organisation.issueTypes ?? {}) as Record<
|
||||||
|
string,
|
||||||
|
{ icon: string; color: string }
|
||||||
|
>;
|
||||||
|
|
||||||
const issues = useMemo(() => [...issuesData].reverse(), [issuesData]);
|
const issues = useMemo(() => [...issuesData].reverse(), [issuesData]);
|
||||||
|
|
||||||
@@ -90,6 +95,14 @@ export function IssuesTable({
|
|||||||
onClick={handleLinkClick}
|
onClick={handleLinkClick}
|
||||||
className="flex items-center gap-2 min-w-0 w-full h-full px-2 py-1 text-inherit hover:underline decoration-transparent"
|
className="flex items-center gap-2 min-w-0 w-full h-full px-2 py-1 text-inherit hover:underline decoration-transparent"
|
||||||
>
|
>
|
||||||
|
{selectedOrganisation?.Organisation.features.issueTypes &&
|
||||||
|
issueTypes[issueData.Issue.type] && (
|
||||||
|
<Icon
|
||||||
|
icon={issueTypes[issueData.Issue.type].icon as IconName}
|
||||||
|
size={16}
|
||||||
|
color={issueTypes[issueData.Issue.type].color}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{selectedOrganisation?.Organisation.features.issueStatus &&
|
{selectedOrganisation?.Organisation.features.issueStatus &&
|
||||||
(columns.status == null || columns.status === true) && (
|
(columns.status == null || columns.status === true) && (
|
||||||
<StatusTag status={issueData.Issue.status} colour={statuses[issueData.Issue.status]} />
|
<StatusTag status={issueData.Issue.status} colour={statuses[issueData.Issue.status]} />
|
||||||
|
|||||||
59
packages/frontend/src/components/type-select.tsx
Normal file
59
packages/frontend/src/components/type-select.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Icon, { type IconName } from "@/components/ui/icon";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
|
|
||||||
|
type IssueTypeConfig = {
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function TypeSelect({
|
||||||
|
issueTypes,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder = "Select type",
|
||||||
|
trigger,
|
||||||
|
}: {
|
||||||
|
issueTypes: Record<string, IssueTypeConfig>;
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
trigger?: (args: { isOpen: boolean; value: string }) => ReactNode;
|
||||||
|
}) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const selectedType = issueTypes[value];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select value={value} onValueChange={onChange} onOpenChange={setIsOpen}>
|
||||||
|
{trigger ? (
|
||||||
|
trigger({ isOpen, value })
|
||||||
|
) : (
|
||||||
|
<SelectTrigger
|
||||||
|
className="w-fit px-2 text-sm gap-1"
|
||||||
|
size="sm"
|
||||||
|
chevronClassName="size-3 -mr-1"
|
||||||
|
isOpen={isOpen}
|
||||||
|
>
|
||||||
|
{selectedType ? (
|
||||||
|
<span className="flex items-center gap-1.5">
|
||||||
|
<Icon icon={selectedType.icon as IconName} size={20} color={selectedType.color} />
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<SelectValue placeholder={placeholder} />
|
||||||
|
)}
|
||||||
|
</SelectTrigger>
|
||||||
|
)}
|
||||||
|
<SelectContent side="bottom" position="popper" align="start">
|
||||||
|
{Object.entries(issueTypes).map(([typeName, typeConfig]) => (
|
||||||
|
<SelectItem key={typeName} value={typeName} textClassName="text-sm">
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Icon icon={typeConfig.icon as IconName} size={20} color={typeConfig.color} />
|
||||||
|
<span>{typeName}</span>
|
||||||
|
</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Alert as PixelAlert,
|
Alert as PixelAlert,
|
||||||
Check as PixelCheck,
|
Check as PixelCheck,
|
||||||
|
Checkbox as PixelCheckbox,
|
||||||
ChevronDown as PixelChevronDown,
|
ChevronDown as PixelChevronDown,
|
||||||
ChevronLeft as PixelChevronLeft,
|
ChevronLeft as PixelChevronLeft,
|
||||||
ChevronRight as PixelChevronRight,
|
ChevronRight as PixelChevronRight,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
Circle as PixelCircle,
|
Circle as PixelCircle,
|
||||||
Clock as PixelClock,
|
Clock as PixelClock,
|
||||||
Close as PixelClose,
|
Close as PixelClose,
|
||||||
|
Debug as PixelDebug,
|
||||||
Edit as PixelEdit,
|
Edit as PixelEdit,
|
||||||
Home as PixelHome,
|
Home as PixelHome,
|
||||||
InfoBox as PixelInfo,
|
InfoBox as PixelInfo,
|
||||||
@@ -26,8 +28,10 @@ import {
|
|||||||
ViewportWide as PixelViewportWide,
|
ViewportWide as PixelViewportWide,
|
||||||
} from "@nsmr/pixelart-react";
|
} from "@nsmr/pixelart-react";
|
||||||
import {
|
import {
|
||||||
|
BugIcon as PhosphorBug,
|
||||||
CheckIcon as PhosphorCheck,
|
CheckIcon as PhosphorCheck,
|
||||||
CheckCircleIcon as PhosphorCheckCircle,
|
CheckCircleIcon as PhosphorCheckCircle,
|
||||||
|
CheckSquareIcon as PhosphorCheckSquare,
|
||||||
CaretDownIcon as PhosphorChevronDown,
|
CaretDownIcon as PhosphorChevronDown,
|
||||||
CaretLeftIcon as PhosphorChevronLeft,
|
CaretLeftIcon as PhosphorChevronLeft,
|
||||||
CaretRightIcon as PhosphorChevronRight,
|
CaretRightIcon as PhosphorChevronRight,
|
||||||
@@ -59,6 +63,7 @@ import {
|
|||||||
import type { IconStyle } from "@sprint/shared";
|
import type { IconStyle } from "@sprint/shared";
|
||||||
import {
|
import {
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
|
Bug,
|
||||||
Check,
|
Check,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
@@ -84,6 +89,7 @@ import {
|
|||||||
OctagonXIcon,
|
OctagonXIcon,
|
||||||
Plus,
|
Plus,
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
|
SquareCheck,
|
||||||
Sun,
|
Sun,
|
||||||
Timer,
|
Timer,
|
||||||
Trash,
|
Trash,
|
||||||
@@ -95,10 +101,14 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useSessionSafe } from "@/components/session-provider";
|
import { useSessionSafe } from "@/components/session-provider";
|
||||||
|
|
||||||
|
// lucide: https://lucide.dev/icons
|
||||||
|
// pixel: https://pixelarticons.com/ (CLICK "Legacy") - these ones are free
|
||||||
const icons = {
|
const icons = {
|
||||||
alertTriangle: { lucide: AlertTriangle, pixel: PixelAlert, phosphor: PhosphorWarning },
|
alertTriangle: { lucide: AlertTriangle, pixel: PixelAlert, phosphor: PhosphorWarning },
|
||||||
|
bug: { lucide: Bug, pixel: PixelDebug, phosphor: PhosphorBug },
|
||||||
check: { lucide: Check, pixel: PixelCheck, phosphor: PhosphorCheck },
|
check: { lucide: Check, pixel: PixelCheck, phosphor: PhosphorCheck },
|
||||||
checkIcon: { lucide: CheckIcon, pixel: PixelCheck, phosphor: PhosphorCheck },
|
checkIcon: { lucide: CheckIcon, pixel: PixelCheck, phosphor: PhosphorCheck },
|
||||||
|
checkBox: { lucide: SquareCheck, pixel: PixelCheckbox, phosphor: PhosphorCheckSquare },
|
||||||
chevronDown: { lucide: ChevronDown, pixel: PixelChevronDown, phosphor: PhosphorChevronDown },
|
chevronDown: { lucide: ChevronDown, pixel: PixelChevronDown, phosphor: PhosphorChevronDown },
|
||||||
chevronDownIcon: { lucide: ChevronDownIcon, pixel: PixelChevronDown, phosphor: PhosphorChevronDown },
|
chevronDownIcon: { lucide: ChevronDownIcon, pixel: PixelChevronDown, phosphor: PhosphorChevronDown },
|
||||||
chevronLeftIcon: { lucide: ChevronLeftIcon, pixel: PixelChevronLeft, phosphor: PhosphorChevronLeft },
|
chevronLeftIcon: { lucide: ChevronLeftIcon, pixel: PixelChevronLeft, phosphor: PhosphorChevronLeft },
|
||||||
@@ -149,6 +159,7 @@ export default function Icon({
|
|||||||
icon,
|
icon,
|
||||||
iconStyle,
|
iconStyle,
|
||||||
size = 24,
|
size = 24,
|
||||||
|
color,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
icon: IconName;
|
icon: IconName;
|
||||||
@@ -174,12 +185,14 @@ export default function Icon({
|
|||||||
<IconComponent
|
<IconComponent
|
||||||
size={size}
|
size={size}
|
||||||
fill={
|
fill={
|
||||||
(resolvedStyle === "pixel" && icon === "moon") ||
|
color
|
||||||
(resolvedStyle === "pixel" && icon === "hash") ||
|
? color
|
||||||
resolvedStyle === "phosphor"
|
: (resolvedStyle === "pixel" && ["bug", "moon", "hash"].includes(icon)) ||
|
||||||
? "var(--foreground)"
|
resolvedStyle === "phosphor"
|
||||||
: "transparent"
|
? "var(--foreground)"
|
||||||
|
: "transparent"
|
||||||
}
|
}
|
||||||
|
style={{ color: color ? color : "var(--foreground)" }}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import "./App.css";
|
import "./App.css";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
import { QueryProvider } from "@/components/query-provider";
|
import { QueryProvider } from "@/components/query-provider";
|
||||||
import { SelectionProvider } from "@/components/selection-provider";
|
import { SelectionProvider } from "@/components/selection-provider";
|
||||||
import { RequireAuth, SessionProvider } from "@/components/session-provider";
|
import { RequireAuth, SessionProvider } from "@/components/session-provider";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
ISSUE_DESCRIPTION_MAX_LENGTH,
|
ISSUE_DESCRIPTION_MAX_LENGTH,
|
||||||
ISSUE_STATUS_MAX_LENGTH,
|
ISSUE_STATUS_MAX_LENGTH,
|
||||||
ISSUE_TITLE_MAX_LENGTH,
|
ISSUE_TITLE_MAX_LENGTH,
|
||||||
|
ISSUE_TYPE_MAX_LENGTH,
|
||||||
ORG_DESCRIPTION_MAX_LENGTH,
|
ORG_DESCRIPTION_MAX_LENGTH,
|
||||||
ORG_NAME_MAX_LENGTH,
|
ORG_NAME_MAX_LENGTH,
|
||||||
ORG_SLUG_MAX_LENGTH,
|
ORG_SLUG_MAX_LENGTH,
|
||||||
@@ -66,9 +67,10 @@ export type AuthResponse = z.infer<typeof AuthResponseSchema>;
|
|||||||
|
|
||||||
export const IssueCreateRequestSchema = z.object({
|
export const IssueCreateRequestSchema = z.object({
|
||||||
projectId: z.number().int().positive("projectId must be a positive integer"),
|
projectId: z.number().int().positive("projectId must be a positive integer"),
|
||||||
|
type: z.string().max(ISSUE_TYPE_MAX_LENGTH).optional(),
|
||||||
|
status: z.string().max(ISSUE_STATUS_MAX_LENGTH).optional(),
|
||||||
title: z.string().min(1, "Title is required").max(ISSUE_TITLE_MAX_LENGTH),
|
title: z.string().min(1, "Title is required").max(ISSUE_TITLE_MAX_LENGTH),
|
||||||
description: z.string().max(ISSUE_DESCRIPTION_MAX_LENGTH).default(""),
|
description: z.string().max(ISSUE_DESCRIPTION_MAX_LENGTH).default(""),
|
||||||
status: z.string().max(ISSUE_STATUS_MAX_LENGTH).optional(),
|
|
||||||
assigneeIds: z.array(z.number().int().positive()).optional(),
|
assigneeIds: z.array(z.number().int().positive()).optional(),
|
||||||
sprintId: z.number().int().positive().nullable().optional(),
|
sprintId: z.number().int().positive().nullable().optional(),
|
||||||
});
|
});
|
||||||
@@ -77,9 +79,10 @@ export type IssueCreateRequest = z.infer<typeof IssueCreateRequestSchema>;
|
|||||||
|
|
||||||
export const IssueUpdateRequestSchema = z.object({
|
export const IssueUpdateRequestSchema = z.object({
|
||||||
id: z.number().int().positive("id must be a positive integer"),
|
id: z.number().int().positive("id must be a positive integer"),
|
||||||
|
type: z.string().max(ISSUE_TYPE_MAX_LENGTH).optional(),
|
||||||
|
status: z.string().max(ISSUE_STATUS_MAX_LENGTH).optional(),
|
||||||
title: z.string().min(1, "Title must be at least 1 character").max(ISSUE_TITLE_MAX_LENGTH).optional(),
|
title: z.string().min(1, "Title must be at least 1 character").max(ISSUE_TITLE_MAX_LENGTH).optional(),
|
||||||
description: z.string().max(ISSUE_DESCRIPTION_MAX_LENGTH).optional(),
|
description: z.string().max(ISSUE_DESCRIPTION_MAX_LENGTH).optional(),
|
||||||
status: z.string().max(ISSUE_STATUS_MAX_LENGTH).optional(),
|
|
||||||
assigneeIds: z.array(z.number().int().positive()).nullable().optional(),
|
assigneeIds: z.array(z.number().int().positive()).nullable().optional(),
|
||||||
sprintId: z.number().int().positive().nullable().optional(),
|
sprintId: z.number().int().positive().nullable().optional(),
|
||||||
});
|
});
|
||||||
@@ -388,9 +391,10 @@ export const IssueRecordSchema = z.object({
|
|||||||
id: z.number(),
|
id: z.number(),
|
||||||
projectId: z.number(),
|
projectId: z.number(),
|
||||||
number: z.number(),
|
number: z.number(),
|
||||||
|
type: z.string(),
|
||||||
|
status: z.string(),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
status: z.string(),
|
|
||||||
creatorId: z.number(),
|
creatorId: z.number(),
|
||||||
sprintId: z.number().nullable(),
|
sprintId: z.number().nullable(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ export const PROJECT_SLUG_MAX_LENGTH = 64;
|
|||||||
|
|
||||||
export const ISSUE_TITLE_MAX_LENGTH = 64;
|
export const ISSUE_TITLE_MAX_LENGTH = 64;
|
||||||
export const ISSUE_DESCRIPTION_MAX_LENGTH = 2048;
|
export const ISSUE_DESCRIPTION_MAX_LENGTH = 2048;
|
||||||
|
export const ISSUE_TYPE_MAX_LENGTH = 16;
|
||||||
export const ISSUE_STATUS_MAX_LENGTH = 24;
|
export const ISSUE_STATUS_MAX_LENGTH = 24;
|
||||||
export const ISSUE_COMMENT_MAX_LENGTH = 2048;
|
export const ISSUE_COMMENT_MAX_LENGTH = 2048;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ISSUE_DESCRIPTION_MAX_LENGTH,
|
ISSUE_DESCRIPTION_MAX_LENGTH,
|
||||||
ISSUE_STATUS_MAX_LENGTH,
|
ISSUE_STATUS_MAX_LENGTH,
|
||||||
ISSUE_TITLE_MAX_LENGTH,
|
ISSUE_TITLE_MAX_LENGTH,
|
||||||
|
ISSUE_TYPE_MAX_LENGTH,
|
||||||
ORG_DESCRIPTION_MAX_LENGTH,
|
ORG_DESCRIPTION_MAX_LENGTH,
|
||||||
ORG_NAME_MAX_LENGTH,
|
ORG_NAME_MAX_LENGTH,
|
||||||
ORG_SLUG_MAX_LENGTH,
|
ORG_SLUG_MAX_LENGTH,
|
||||||
@@ -28,9 +29,15 @@ export const DEFAULT_STATUS_COLOURS: Record<string, string> = {
|
|||||||
MERGED: DEFAULT_STATUS_COLOUR,
|
MERGED: DEFAULT_STATUS_COLOUR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_ISSUE_TYPES: Record<string, { icon: string; color: string }> = {
|
||||||
|
Task: { icon: "checkBox", color: "#e4bd47" },
|
||||||
|
Bug: { icon: "bug", color: "#ef4444" },
|
||||||
|
};
|
||||||
|
|
||||||
export const DEFAULT_FEATURES: Record<string, boolean> = {
|
export const DEFAULT_FEATURES: Record<string, boolean> = {
|
||||||
userAvatars: true,
|
userAvatars: true,
|
||||||
|
|
||||||
|
issueTypes: true,
|
||||||
issueStatus: true,
|
issueStatus: true,
|
||||||
issueDescriptions: true,
|
issueDescriptions: true,
|
||||||
issueTimeTracking: true,
|
issueTimeTracking: true,
|
||||||
@@ -63,6 +70,10 @@ export const Organisation = pgTable("Organisation", {
|
|||||||
slug: varchar({ length: ORG_SLUG_MAX_LENGTH }).notNull().unique(),
|
slug: varchar({ length: ORG_SLUG_MAX_LENGTH }).notNull().unique(),
|
||||||
iconURL: varchar({ length: 512 }),
|
iconURL: varchar({ length: 512 }),
|
||||||
statuses: json("statuses").$type<Record<string, string>>().notNull().default(DEFAULT_STATUS_COLOURS),
|
statuses: json("statuses").$type<Record<string, string>>().notNull().default(DEFAULT_STATUS_COLOURS),
|
||||||
|
issueTypes: json("issueTypes")
|
||||||
|
.$type<Record<string, { icon: string; color: string }>>()
|
||||||
|
.notNull()
|
||||||
|
.default(DEFAULT_ISSUE_TYPES),
|
||||||
features: json("features").$type<Record<string, boolean>>().notNull().default(DEFAULT_FEATURES),
|
features: json("features").$type<Record<string, boolean>>().notNull().default(DEFAULT_FEATURES),
|
||||||
createdAt: timestamp({ withTimezone: false }).defaultNow(),
|
createdAt: timestamp({ withTimezone: false }).defaultNow(),
|
||||||
updatedAt: timestamp({ withTimezone: false }).defaultNow(),
|
updatedAt: timestamp({ withTimezone: false }).defaultNow(),
|
||||||
@@ -135,9 +146,10 @@ export const Issue = pgTable(
|
|||||||
|
|
||||||
number: integer("number").notNull(),
|
number: integer("number").notNull(),
|
||||||
|
|
||||||
|
type: varchar({ length: ISSUE_TYPE_MAX_LENGTH }).notNull().default("Task"),
|
||||||
|
status: varchar({ length: ISSUE_STATUS_MAX_LENGTH }).notNull().default("TO DO"),
|
||||||
title: varchar({ length: ISSUE_TITLE_MAX_LENGTH }).notNull(),
|
title: varchar({ length: ISSUE_TITLE_MAX_LENGTH }).notNull(),
|
||||||
description: varchar({ length: ISSUE_DESCRIPTION_MAX_LENGTH }).notNull(),
|
description: varchar({ length: ISSUE_DESCRIPTION_MAX_LENGTH }).notNull(),
|
||||||
status: varchar({ length: ISSUE_STATUS_MAX_LENGTH }).notNull().default("TO DO"),
|
|
||||||
|
|
||||||
creatorId: integer()
|
creatorId: integer()
|
||||||
.notNull()
|
.notNull()
|
||||||
|
|||||||
13
todo.md
13
todo.md
@@ -1,28 +1,25 @@
|
|||||||
# HIGH PRIORITY
|
# HIGH PRIORITY
|
||||||
|
|
||||||
- FEATURES:
|
- FEATURES:
|
||||||
- issues
|
- org settings:
|
||||||
- issue type (options stored on Organisation)
|
- manage issue types
|
||||||
|
- create, edit, delete
|
||||||
|
- assign icons to issue types (ensure each available icon is in EACH icon set)
|
||||||
- filters
|
- filters
|
||||||
- time tracking:
|
- time tracking:
|
||||||
- add overlay in the bottom left for active timers if there are any. this should be minimal with the issue key (API-005), the time, and a play/pause + end button
|
- add overlay in the bottom left for active timers if there are any. this should be minimal with the issue key (API-005), the time, and a play/pause + end button
|
||||||
- pricing page
|
- pricing page
|
||||||
- see jira and other competitors
|
- see jira and other competitors
|
||||||
- explore payment providers (stripe is the only one i know)
|
- explore payment providers (stripe is the only one i know)
|
||||||
|
- add "modal=true" in issue urls that are copied, and open issue-modal.tsx instead of the issue-detail-pane.tsx
|
||||||
|
|
||||||
# LOW PRIORITY
|
# LOW PRIORITY
|
||||||
|
|
||||||
- dedicated /register route (currently login/register are combined on /login)
|
- dedicated /register route (currently login/register are combined on /login)
|
||||||
- real logo
|
- real logo
|
||||||
- org settings
|
|
||||||
- manage issue types, default is [bug, feature]
|
|
||||||
- create, edit, delete
|
|
||||||
- assign icons to issue types (ensure each available icon is in EACH icon set)
|
|
||||||
- issues
|
- issues
|
||||||
- assignee "note" for extra context on their role in the task
|
- assignee "note" for extra context on their role in the task
|
||||||
- deadline
|
- deadline
|
||||||
- comments
|
|
||||||
- admins are capable of deleting comments from members who are at their permission level or below (not sure if this should apply, or if ANYONE should have control over others' comments - people in an org tend to be trusted to not be trolls)
|
|
||||||
- user preferences
|
- user preferences
|
||||||
- colour scheme
|
- colour scheme
|
||||||
- "assign to me by default" option for new issues
|
- "assign to me by default" option for new issues
|
||||||
|
|||||||
Reference in New Issue
Block a user