Skip to content

Commit 30ccb1e

Browse files
Merge pull request #42 from nashtech-garage/dev
Dev
2 parents 1c9c894 + 900c6e9 commit 30ccb1e

File tree

126 files changed

+7472
-2551
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+7472
-2551
lines changed

.github/workflows/frontend-ci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ jobs:
5050
- name: Build & Deploy
5151
run: |
5252
npm run build
53-
npx vercel --prod --yes --token=${{ secrets.TRELLO_FRONTEND_VERCEL_TOKEN }}
54-
env:
55-
VERCEL_ORG_ID: ${{ vars.TRELLO_FRONTEND_VERCEL_ORG_ID }}
56-
VERCEL_PROJECT_ID: ${{ vars.TRELLO_FRONTEND_VERCEL_PROJECT_ID }}
57-
VITE_TRELLO_LIKE_API_URL: ${{ vars.VITE_TRELLO_LIKE_API_URL }}
53+
# npx vercel --prod --yes --token=${{ secrets.TRELLO_FRONTEND_VERCEL_TOKEN }}
54+
# env:
55+
# VERCEL_ORG_ID: ${{ vars.TRELLO_FRONTEND_VERCEL_ORG_ID }}
56+
# VERCEL_PROJECT_ID: ${{ vars.TRELLO_FRONTEND_VERCEL_PROJECT_ID }}
57+
# VITE_TRELLO_LIKE_API_URL: ${{ vars.VITE_TRELLO_LIKE_API_URL }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
.vscode

backend/api-specs/api_specs.yaml

Lines changed: 168 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,13 +1431,19 @@ paths:
14311431
example:
14321432
message: "Column not found"
14331433

1434-
/columns/{columnId}/position:
1434+
/projects/{projectId}/columns/{columnId}/position:
14351435
patch:
14361436
tags:
14371437
- Columns
14381438
summary: Update column position
14391439
description: Updates the position of a specific column in a project
14401440
parameters:
1441+
- in: path
1442+
name: projectId
1443+
required: true
1444+
description: Project ID
1445+
schema:
1446+
type: integer
14411447
- in: path
14421448
name: columnId
14431449
required: true
@@ -1497,7 +1503,7 @@ paths:
14971503

14981504

14991505
/projects/{projectId}/columns/archived:
1500-
get:
1506+
get:
15011507
tags:
15021508
- Columns
15031509
summary: Get archived columns in project
@@ -1526,12 +1532,12 @@ paths:
15261532
example:
15271533
message: "Archived columns retrieved"
15281534
data:
1529-
- id: 2
1530-
name: "Done"
1531-
position: 3
1532-
- id: 3
1533-
name: "Archived"
1534-
position: 4
1535+
- id: 2
1536+
name: "Done"
1537+
position: 3
1538+
- id: 3
1539+
name: "Archived"
1540+
position: 4
15351541

15361542
'401':
15371543
description: Unauthenticated request
@@ -1966,6 +1972,128 @@ paths:
19661972
example:
19671973
message: "Task with ID 22 does not exist"
19681974

1975+
/projects/{projectId}/columns/tasks/archived:
1976+
get:
1977+
tags:
1978+
- Tasks
1979+
summary: Get archived tasks in project
1980+
description: Retrieves all archived tasks in the specified project
1981+
parameters:
1982+
- in: path
1983+
name: projectId
1984+
required: true
1985+
description: Project ID
1986+
schema:
1987+
type: integer
1988+
responses:
1989+
'200':
1990+
description: Archived tasks retrieved successfully
1991+
content:
1992+
application/json:
1993+
schema:
1994+
allOf:
1995+
- $ref: '#/components/schemas/ApiResponse'
1996+
- type: object
1997+
properties:
1998+
data:
1999+
type: array
2000+
items:
2001+
$ref: '#/components/schemas/TaskSummariesResponse'
2002+
example:
2003+
message: "Archived tasks retrieved successfully"
2004+
data:
2005+
- id: 2
2006+
name: "Task A"
2007+
position: 3
2008+
columnId: 1
2009+
- id: 3
2010+
name: "Task B"
2011+
position: 4
2012+
columnId: 2
2013+
2014+
'401':
2015+
description: Unauthenticated request
2016+
content:
2017+
application/json:
2018+
schema:
2019+
allOf:
2020+
- $ref: '#/components/schemas/ApiResponse'
2021+
- type: object
2022+
example:
2023+
message: "Authentication required"
2024+
2025+
'404':
2026+
description: Project not found
2027+
content:
2028+
application/json:
2029+
schema:
2030+
allOf:
2031+
- $ref: '#/components/schemas/ApiResponse'
2032+
- type: object
2033+
example:
2034+
message: "Project not found"
2035+
2036+
/projects/{projectId}/columns/tasks/active:
2037+
get:
2038+
tags:
2039+
- Tasks
2040+
summary: Get active tasks in project
2041+
description: Retrieves all active tasks in the specified project
2042+
parameters:
2043+
- in: path
2044+
name: projectId
2045+
required: true
2046+
description: Project ID
2047+
schema:
2048+
type: integer
2049+
responses:
2050+
'200':
2051+
description: Active tasks retrieved successfully
2052+
content:
2053+
application/json:
2054+
schema:
2055+
allOf:
2056+
- $ref: '#/components/schemas/ApiResponse'
2057+
- type: object
2058+
properties:
2059+
data:
2060+
type: array
2061+
items:
2062+
$ref: '#/components/schemas/TaskSummariesResponse'
2063+
example:
2064+
message: "Archived tasks retrieved successfully"
2065+
data:
2066+
- id: 2
2067+
name: "Task A"
2068+
position: 3
2069+
columnId: 1
2070+
- id: 3
2071+
name: "Task B"
2072+
position: 4
2073+
columnId: 2
2074+
2075+
'401':
2076+
description: Unauthenticated request
2077+
content:
2078+
application/json:
2079+
schema:
2080+
allOf:
2081+
- $ref: '#/components/schemas/ApiResponse'
2082+
- type: object
2083+
example:
2084+
message: "Authentication required"
2085+
2086+
'404':
2087+
description: Project not found
2088+
content:
2089+
application/json:
2090+
schema:
2091+
allOf:
2092+
- $ref: '#/components/schemas/ApiResponse'
2093+
- type: object
2094+
example:
2095+
message: "Project not found"
2096+
19692097
components:
19702098
schemas:
19712099
# Request schemas
@@ -2154,15 +2282,15 @@ components:
21542282
example: false
21552283

21562284
UpdateColumnPositionRequest:
2157-
type: object
2158-
required:
2159-
- position
2160-
properties:
2161-
position:
2162-
type: integer
2163-
minimum: 1
2164-
description: New position of the column in the project board
2165-
example: 2
2285+
type: object
2286+
required:
2287+
- position
2288+
properties:
2289+
position:
2290+
type: integer
2291+
minimum: 1
2292+
description: New position of the column in the project board
2293+
example: 2
21662294

21672295

21682296
# Response schemas
@@ -2317,6 +2445,10 @@ components:
23172445
type: integer
23182446
description: Position of the task in the column
23192447
example: 1
2448+
columnId:
2449+
type: integer
2450+
description: ID of the column containing the task
2451+
example: 1
23202452

23212453
ColumnWithTasksResponse:
23222454
type: object
@@ -2409,6 +2541,25 @@ components:
24092541
type: integer
24102542
description: Position of the column in the project board
24112543
example: 1
2544+
TaskSummariesResponse:
2545+
type: object
2546+
properties:
2547+
id:
2548+
type: integer
2549+
description: Task ID
2550+
example: 1
2551+
name:
2552+
type: string
2553+
description: Task name
2554+
example: "Task A"
2555+
position:
2556+
type: integer
2557+
description: Position of the task in the column
2558+
example: 1
2559+
columnId:
2560+
type: integer
2561+
description: ID of the column containing the task
2562+
example: 1
24122563

24132564
ProjectResponse:
24142565
type: object
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package actors
2+
3+
import dto.websocket.OutgoingMessage
4+
import org.apache.pekko.actor.{Actor, ActorRef, Props}
5+
import play.api.Logger
6+
import play.api.libs.json.{JsValue, Json}
7+
8+
/**
9+
* Actor that manages WebSocket connections for a specific project.
10+
*/
11+
object ProjectActor {
12+
def props(projectId: Int): Props = Props(new ProjectActor(projectId))
13+
14+
case class Join(userId: Int, out: ActorRef)
15+
case class Leave(userId: Int)
16+
case class Broadcast(message: OutgoingMessage)
17+
}
18+
19+
/**
20+
* Actor that manages WebSocket connections for a specific project.
21+
* It keeps track of connected users and broadcasts messages to them.
22+
*
23+
* @param projectId the ID of the project
24+
*/
25+
class ProjectActor(projectId: Int) extends Actor {
26+
import ProjectActor._
27+
28+
private var members = Map.empty[Int, ActorRef]
29+
30+
def receive: Receive = {
31+
case Join(userId, out) =>
32+
members += userId -> out
33+
Logger("actors").info(
34+
s"UserId $userId joined project $projectId. Total members: ${members.size}"
35+
)
36+
37+
case Leave(userId) =>
38+
members -= userId
39+
Logger("actors").info(
40+
s"UserId with $userId left project $projectId. Total members: ${members.size}"
41+
)
42+
43+
case Broadcast(msg) =>
44+
val js: JsValue = Json.toJson(msg)
45+
members.values.foreach(_ ! js)
46+
Logger("actors").info(
47+
s"Broadcasted message to project $projectId members: ${members.size} users"
48+
)
49+
}
50+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package actors
2+
3+
import org.apache.pekko.actor.{Actor, ActorRef, Props}
4+
import dto.websocket.OutgoingMessage
5+
import modules.ActorNames
6+
7+
object ProjectActorRegistry {
8+
def props: Props = Props(new ProjectActorRegistry)
9+
10+
case class GetProjectActor(projectId: Int)
11+
case class BroadcastToProject(projectId: Int, message: OutgoingMessage)
12+
}
13+
14+
class ProjectActorRegistry extends Actor {
15+
import ProjectActorRegistry._
16+
import ProjectActor._
17+
18+
private var projectActors = Map.empty[Int, ActorRef]
19+
20+
def receive: Receive = {
21+
case GetProjectActor(projectId) =>
22+
// create new ProjectActor if not exists
23+
val actor = projectActors.getOrElse(projectId, {
24+
val newActor = context.actorOf(ProjectActor.props(projectId), s"${ActorNames.ProjectActorPrefix}$projectId")
25+
projectActors += projectId -> newActor
26+
newActor
27+
})
28+
// return ActorRef for requester
29+
sender() ! actor
30+
31+
case BroadcastToProject(projectId, message) =>
32+
projectActors.get(projectId).foreach(_ ! Broadcast(message))
33+
}
34+
}

0 commit comments

Comments
 (0)