Skip to content

Commit 409dabf

Browse files
authored
Merge pull request #152 from Zindiks/claude/multi-select-bulk-actions-01B5LK362dbme4CaYC2TSJtP
docs: add comprehensive bulk actions & multi-select implementation plan
2 parents fa7e592 + f23bbc7 commit 409dabf

27 files changed

+5357
-25
lines changed

BULK_ACTIONS_IMPLEMENTATION_PLAN.md

Lines changed: 1422 additions & 0 deletions
Large diffs are not rendered by default.

BULK_ACTIONS_PHASES_SUMMARY.md

Lines changed: 413 additions & 0 deletions
Large diffs are not rendered by default.

PHASE_5_POLISH_PLAN.md

Lines changed: 1038 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import { FastifyReply, FastifyRequest } from "fastify";
2+
import {
3+
BulkService,
4+
BulkMoveCardsInput,
5+
BulkAssignUsersInput,
6+
BulkAddLabelsInput,
7+
BulkSetDueDateInput,
8+
BulkArchiveCardsInput,
9+
BulkDeleteCardsInput,
10+
} from "./bulk.service";
11+
import { getWebSocketService } from "../../bootstrap";
12+
13+
export class BulkController {
14+
private readonly bulkService: BulkService;
15+
16+
constructor() {
17+
this.bulkService = new BulkService();
18+
}
19+
20+
/**
21+
* POST /cards/bulk/move
22+
* Move multiple cards to a target list
23+
*/
24+
public async bulkMoveCards(
25+
request: FastifyRequest<{
26+
Body: BulkMoveCardsInput;
27+
}>,
28+
reply: FastifyReply,
29+
) {
30+
try {
31+
const { card_ids, target_list_id } = request.body;
32+
33+
if (!card_ids || card_ids.length === 0) {
34+
return reply.status(400).send({ message: "card_ids is required" });
35+
}
36+
37+
if (!target_list_id) {
38+
return reply.status(400).send({ message: "target_list_id is required" });
39+
}
40+
41+
const result = await this.bulkService.moveCards(request.body);
42+
43+
// Broadcast WebSocket event
44+
const wsService = getWebSocketService();
45+
if (wsService) {
46+
wsService.broadcastToBoard("board_update", {
47+
type: "BULK_CARDS_MOVED",
48+
payload: {
49+
card_ids,
50+
target_list_id,
51+
},
52+
});
53+
}
54+
55+
return reply.status(200).send(result);
56+
} catch (err) {
57+
console.error("Bulk move cards error:", err);
58+
return reply.status(500).send({ message: "Internal server error" });
59+
}
60+
}
61+
62+
/**
63+
* POST /cards/bulk/assign
64+
* Assign users to multiple cards
65+
*/
66+
public async bulkAssignUsers(
67+
request: FastifyRequest<{
68+
Body: BulkAssignUsersInput;
69+
}>,
70+
reply: FastifyReply,
71+
) {
72+
try {
73+
const { card_ids, user_ids } = request.body;
74+
75+
if (!card_ids || card_ids.length === 0) {
76+
return reply.status(400).send({ message: "card_ids is required" });
77+
}
78+
79+
if (!user_ids || user_ids.length === 0) {
80+
return reply.status(400).send({ message: "user_ids is required" });
81+
}
82+
83+
const result = await this.bulkService.assignUsers(request.body);
84+
85+
// Broadcast WebSocket event
86+
const wsService = getWebSocketService();
87+
if (wsService) {
88+
wsService.broadcastToBoard("board_update", {
89+
type: "BULK_USERS_ASSIGNED",
90+
payload: {
91+
card_ids,
92+
user_ids,
93+
},
94+
});
95+
}
96+
97+
return reply.status(200).send(result);
98+
} catch (err) {
99+
console.error("Bulk assign users error:", err);
100+
return reply.status(500).send({ message: "Internal server error" });
101+
}
102+
}
103+
104+
/**
105+
* POST /cards/bulk/labels
106+
* Add labels to multiple cards
107+
*/
108+
public async bulkAddLabels(
109+
request: FastifyRequest<{
110+
Body: BulkAddLabelsInput;
111+
}>,
112+
reply: FastifyReply,
113+
) {
114+
try {
115+
const { card_ids, label_ids } = request.body;
116+
117+
if (!card_ids || card_ids.length === 0) {
118+
return reply.status(400).send({ message: "card_ids is required" });
119+
}
120+
121+
if (!label_ids || label_ids.length === 0) {
122+
return reply.status(400).send({ message: "label_ids is required" });
123+
}
124+
125+
const result = await this.bulkService.addLabels(request.body);
126+
127+
// Broadcast WebSocket event
128+
const wsService = getWebSocketService();
129+
if (wsService) {
130+
wsService.broadcastToBoard("board_update", {
131+
type: "BULK_LABELS_ADDED",
132+
payload: {
133+
card_ids,
134+
label_ids,
135+
},
136+
});
137+
}
138+
139+
return reply.status(200).send(result);
140+
} catch (err) {
141+
console.error("Bulk add labels error:", err);
142+
return reply.status(500).send({ message: "Internal server error" });
143+
}
144+
}
145+
146+
/**
147+
* POST /cards/bulk/due-date
148+
* Set due date on multiple cards
149+
*/
150+
public async bulkSetDueDate(
151+
request: FastifyRequest<{
152+
Body: BulkSetDueDateInput;
153+
}>,
154+
reply: FastifyReply,
155+
) {
156+
try {
157+
const { card_ids, due_date } = request.body;
158+
159+
if (!card_ids || card_ids.length === 0) {
160+
return reply.status(400).send({ message: "card_ids is required" });
161+
}
162+
163+
const result = await this.bulkService.setDueDate(request.body);
164+
165+
// Broadcast WebSocket event
166+
const wsService = getWebSocketService();
167+
if (wsService) {
168+
wsService.broadcastToBoard("board_update", {
169+
type: "BULK_DUE_DATE_SET",
170+
payload: {
171+
card_ids,
172+
due_date,
173+
},
174+
});
175+
}
176+
177+
return reply.status(200).send(result);
178+
} catch (err) {
179+
console.error("Bulk set due date error:", err);
180+
return reply.status(500).send({ message: "Internal server error" });
181+
}
182+
}
183+
184+
/**
185+
* POST /cards/bulk/archive
186+
* Archive multiple cards
187+
*/
188+
public async bulkArchiveCards(
189+
request: FastifyRequest<{
190+
Body: BulkArchiveCardsInput;
191+
}>,
192+
reply: FastifyReply,
193+
) {
194+
try {
195+
const { card_ids } = request.body;
196+
197+
if (!card_ids || card_ids.length === 0) {
198+
return reply.status(400).send({ message: "card_ids is required" });
199+
}
200+
201+
const result = await this.bulkService.archiveCards(request.body);
202+
203+
// Broadcast WebSocket event
204+
const wsService = getWebSocketService();
205+
if (wsService) {
206+
wsService.broadcastToBoard("board_update", {
207+
type: "BULK_CARDS_ARCHIVED",
208+
payload: {
209+
card_ids,
210+
},
211+
});
212+
}
213+
214+
return reply.status(200).send(result);
215+
} catch (err) {
216+
console.error("Bulk archive cards error:", err);
217+
return reply.status(500).send({ message: "Internal server error" });
218+
}
219+
}
220+
221+
/**
222+
* DELETE /cards/bulk
223+
* Delete multiple cards
224+
*/
225+
public async bulkDeleteCards(
226+
request: FastifyRequest<{
227+
Body: BulkDeleteCardsInput;
228+
}>,
229+
reply: FastifyReply,
230+
) {
231+
try {
232+
const { card_ids } = request.body;
233+
234+
if (!card_ids || card_ids.length === 0) {
235+
return reply.status(400).send({ message: "card_ids is required" });
236+
}
237+
238+
const result = await this.bulkService.deleteCards(request.body);
239+
240+
// Broadcast WebSocket event
241+
const wsService = getWebSocketService();
242+
if (wsService) {
243+
wsService.broadcastToBoard("board_update", {
244+
type: "BULK_CARDS_DELETED",
245+
payload: {
246+
card_ids,
247+
},
248+
});
249+
}
250+
251+
return reply.status(200).send(result);
252+
} catch (err) {
253+
console.error("Bulk delete cards error:", err);
254+
return reply.status(500).send({ message: "Internal server error" });
255+
}
256+
}
257+
}

0 commit comments

Comments
 (0)