@nextsparkjs/theme-productivity 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/about.md +123 -0
- package/components/CardDetailModal.tsx +318 -0
- package/components/KanbanBoard.tsx +612 -0
- package/components/KanbanCard.tsx +218 -0
- package/components/KanbanColumn.tsx +264 -0
- package/components/SortableList.tsx +46 -0
- package/components/index.ts +4 -0
- package/config/app.config.ts +172 -0
- package/config/billing.config.ts +187 -0
- package/config/dashboard.config.ts +357 -0
- package/config/dev.config.ts +55 -0
- package/config/features.config.ts +256 -0
- package/config/flows.config.ts +484 -0
- package/config/permissions.config.ts +167 -0
- package/config/theme.config.ts +106 -0
- package/entities/boards/boards.config.ts +61 -0
- package/entities/boards/boards.fields.ts +154 -0
- package/entities/boards/boards.service.ts +256 -0
- package/entities/boards/boards.types.ts +57 -0
- package/entities/boards/messages/en.json +80 -0
- package/entities/boards/messages/es.json +80 -0
- package/entities/boards/migrations/001_boards_table.sql +83 -0
- package/entities/cards/cards.config.ts +61 -0
- package/entities/cards/cards.fields.ts +242 -0
- package/entities/cards/cards.service.ts +336 -0
- package/entities/cards/cards.types.ts +79 -0
- package/entities/cards/messages/en.json +114 -0
- package/entities/cards/messages/es.json +114 -0
- package/entities/cards/migrations/020_cards_table.sql +92 -0
- package/entities/lists/lists.config.ts +61 -0
- package/entities/lists/lists.fields.ts +105 -0
- package/entities/lists/lists.service.ts +252 -0
- package/entities/lists/lists.types.ts +55 -0
- package/entities/lists/messages/en.json +60 -0
- package/entities/lists/messages/es.json +60 -0
- package/entities/lists/migrations/010_lists_table.sql +79 -0
- package/lib/selectors.ts +206 -0
- package/messages/en.json +79 -0
- package/messages/es.json +79 -0
- package/migrations/999_theme_sample_data.sql +922 -0
- package/migrations/999a_initial_sample_data.sql +377 -0
- package/migrations/999b_abundant_sample_data.sql +346 -0
- package/package.json +17 -0
- package/permissions-matrix.md +122 -0
- package/styles/components.css +460 -0
- package/styles/globals.css +560 -0
- package/templates/dashboard/(main)/boards/[id]/[cardId]/page.tsx +238 -0
- package/templates/dashboard/(main)/boards/[id]/edit/page.tsx +390 -0
- package/templates/dashboard/(main)/boards/[id]/page.tsx +236 -0
- package/templates/dashboard/(main)/boards/create/page.tsx +236 -0
- package/templates/dashboard/(main)/boards/page.tsx +335 -0
- package/templates/dashboard/(main)/layout.tsx +32 -0
- package/templates/dashboard/(main)/page.tsx +592 -0
- package/templates/shared/ProductivityMobileNav.tsx +410 -0
- package/templates/shared/ProductivitySidebar.tsx +538 -0
- package/templates/shared/ProductivityTopBar.tsx +317 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cards Service
|
|
3
|
+
* Provides data access methods for cards entity.
|
|
4
|
+
*/
|
|
5
|
+
import { queryOneWithRLS, queryWithRLS, mutateWithRLS } from '@nextsparkjs/core/lib/db'
|
|
6
|
+
|
|
7
|
+
export interface Card {
|
|
8
|
+
id: string
|
|
9
|
+
title: string
|
|
10
|
+
description: string | null
|
|
11
|
+
listId: string
|
|
12
|
+
boardId: string
|
|
13
|
+
position: number
|
|
14
|
+
dueDate: Date | null
|
|
15
|
+
assigneeId: string | null
|
|
16
|
+
labels: string[]
|
|
17
|
+
isArchived: boolean
|
|
18
|
+
userId: string
|
|
19
|
+
teamId: string
|
|
20
|
+
createdAt: Date
|
|
21
|
+
updatedAt: Date
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CardListOptions {
|
|
25
|
+
limit?: number
|
|
26
|
+
offset?: number
|
|
27
|
+
orderBy?: string
|
|
28
|
+
orderDir?: 'asc' | 'desc'
|
|
29
|
+
teamId?: string
|
|
30
|
+
listId?: string
|
|
31
|
+
boardId?: string
|
|
32
|
+
assigneeId?: string
|
|
33
|
+
isArchived?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CardListResult {
|
|
37
|
+
cards: Card[]
|
|
38
|
+
total: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CardCreateData {
|
|
42
|
+
title: string
|
|
43
|
+
description?: string | null
|
|
44
|
+
listId: string
|
|
45
|
+
boardId: string
|
|
46
|
+
position?: number
|
|
47
|
+
dueDate?: Date | null
|
|
48
|
+
assigneeId?: string | null
|
|
49
|
+
labels?: string[]
|
|
50
|
+
isArchived?: boolean
|
|
51
|
+
teamId: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CardUpdateData {
|
|
55
|
+
title?: string
|
|
56
|
+
description?: string | null
|
|
57
|
+
listId?: string
|
|
58
|
+
boardId?: string
|
|
59
|
+
position?: number
|
|
60
|
+
dueDate?: Date | null
|
|
61
|
+
assigneeId?: string | null
|
|
62
|
+
labels?: string[]
|
|
63
|
+
isArchived?: boolean
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface DbCard {
|
|
67
|
+
id: string
|
|
68
|
+
title: string
|
|
69
|
+
description: string | null
|
|
70
|
+
list_id: string
|
|
71
|
+
board_id: string
|
|
72
|
+
position: number
|
|
73
|
+
due_date: string | null
|
|
74
|
+
assignee_id: string | null
|
|
75
|
+
labels: string
|
|
76
|
+
is_archived: boolean
|
|
77
|
+
user_id: string
|
|
78
|
+
team_id: string
|
|
79
|
+
created_at: string
|
|
80
|
+
updated_at: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function mapDbCard(dbCard: DbCard): Card {
|
|
84
|
+
return {
|
|
85
|
+
id: dbCard.id,
|
|
86
|
+
title: dbCard.title,
|
|
87
|
+
description: dbCard.description,
|
|
88
|
+
listId: dbCard.list_id,
|
|
89
|
+
boardId: dbCard.board_id,
|
|
90
|
+
position: dbCard.position,
|
|
91
|
+
dueDate: dbCard.due_date ? new Date(dbCard.due_date) : null,
|
|
92
|
+
assigneeId: dbCard.assignee_id,
|
|
93
|
+
labels: dbCard.labels ? JSON.parse(dbCard.labels) : [],
|
|
94
|
+
isArchived: dbCard.is_archived,
|
|
95
|
+
userId: dbCard.user_id,
|
|
96
|
+
teamId: dbCard.team_id,
|
|
97
|
+
createdAt: new Date(dbCard.created_at),
|
|
98
|
+
updatedAt: new Date(dbCard.updated_at),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class CardsService {
|
|
103
|
+
/**
|
|
104
|
+
* Get a card by ID with RLS
|
|
105
|
+
*/
|
|
106
|
+
static async getById(id: string, userId: string): Promise<Card | null> {
|
|
107
|
+
const result = await queryOneWithRLS<DbCard>(
|
|
108
|
+
userId,
|
|
109
|
+
`SELECT * FROM cards WHERE id = $1`,
|
|
110
|
+
[id]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return result ? mapDbCard(result) : null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* List cards with RLS and filtering
|
|
118
|
+
*/
|
|
119
|
+
static async list(userId: string, options: CardListOptions = {}): Promise<CardListResult> {
|
|
120
|
+
const {
|
|
121
|
+
limit = 50,
|
|
122
|
+
offset = 0,
|
|
123
|
+
orderBy = 'position',
|
|
124
|
+
orderDir = 'asc',
|
|
125
|
+
teamId,
|
|
126
|
+
listId,
|
|
127
|
+
boardId,
|
|
128
|
+
assigneeId,
|
|
129
|
+
isArchived,
|
|
130
|
+
} = options
|
|
131
|
+
|
|
132
|
+
const conditions: string[] = []
|
|
133
|
+
const params: any[] = []
|
|
134
|
+
let paramIndex = 1
|
|
135
|
+
|
|
136
|
+
if (teamId) {
|
|
137
|
+
conditions.push(`team_id = $${paramIndex++}`)
|
|
138
|
+
params.push(teamId)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (listId) {
|
|
142
|
+
conditions.push(`list_id = $${paramIndex++}`)
|
|
143
|
+
params.push(listId)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (boardId) {
|
|
147
|
+
conditions.push(`board_id = $${paramIndex++}`)
|
|
148
|
+
params.push(boardId)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (assigneeId) {
|
|
152
|
+
conditions.push(`assignee_id = $${paramIndex++}`)
|
|
153
|
+
params.push(assigneeId)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (isArchived !== undefined) {
|
|
157
|
+
conditions.push(`is_archived = $${paramIndex++}`)
|
|
158
|
+
params.push(isArchived)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
|
|
162
|
+
|
|
163
|
+
// Get total count
|
|
164
|
+
const countResult = await queryOneWithRLS<{ count: string }>(
|
|
165
|
+
userId,
|
|
166
|
+
`SELECT COUNT(*) as count FROM cards ${whereClause}`,
|
|
167
|
+
params
|
|
168
|
+
)
|
|
169
|
+
const total = parseInt(countResult?.count || '0', 10)
|
|
170
|
+
|
|
171
|
+
// Get cards
|
|
172
|
+
const validOrderBy = ['position', 'title', 'due_date', 'created_at', 'updated_at'].includes(orderBy)
|
|
173
|
+
? orderBy
|
|
174
|
+
: 'position'
|
|
175
|
+
const validOrderDir = orderDir === 'desc' ? 'DESC' : 'ASC'
|
|
176
|
+
|
|
177
|
+
params.push(limit, offset)
|
|
178
|
+
const cards = await queryWithRLS<DbCard>(
|
|
179
|
+
userId,
|
|
180
|
+
`SELECT * FROM cards ${whereClause} ORDER BY ${validOrderBy} ${validOrderDir} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
|
|
181
|
+
params
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
cards: cards.map(mapDbCard),
|
|
186
|
+
total,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get all cards for a specific list
|
|
192
|
+
*/
|
|
193
|
+
static async getByList(listId: string, userId: string): Promise<Card[]> {
|
|
194
|
+
const result = await this.list(userId, {
|
|
195
|
+
listId,
|
|
196
|
+
orderBy: 'position',
|
|
197
|
+
orderDir: 'asc',
|
|
198
|
+
isArchived: false,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
return result.cards
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Create a new card with RLS
|
|
206
|
+
*/
|
|
207
|
+
static async create(userId: string, data: CardCreateData): Promise<Card> {
|
|
208
|
+
const {
|
|
209
|
+
title,
|
|
210
|
+
description = null,
|
|
211
|
+
listId,
|
|
212
|
+
boardId,
|
|
213
|
+
position = 0,
|
|
214
|
+
dueDate = null,
|
|
215
|
+
assigneeId = null,
|
|
216
|
+
labels = [],
|
|
217
|
+
isArchived = false,
|
|
218
|
+
teamId,
|
|
219
|
+
} = data
|
|
220
|
+
|
|
221
|
+
const result = await mutateWithRLS<DbCard>(
|
|
222
|
+
userId,
|
|
223
|
+
`INSERT INTO cards (title, description, list_id, board_id, position, due_date, assignee_id, labels, is_archived, user_id, team_id)
|
|
224
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
225
|
+
RETURNING *`,
|
|
226
|
+
[title, description, listId, boardId, position, dueDate, assigneeId, JSON.stringify(labels), isArchived, userId, teamId]
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if (!result) {
|
|
230
|
+
throw new Error('Failed to create card')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return mapDbCard(result)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Update a card with RLS
|
|
238
|
+
*/
|
|
239
|
+
static async update(userId: string, id: string, data: CardUpdateData): Promise<Card> {
|
|
240
|
+
const updates: string[] = []
|
|
241
|
+
const params: any[] = []
|
|
242
|
+
let paramIndex = 1
|
|
243
|
+
|
|
244
|
+
if (data.title !== undefined) {
|
|
245
|
+
updates.push(`title = $${paramIndex++}`)
|
|
246
|
+
params.push(data.title)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (data.description !== undefined) {
|
|
250
|
+
updates.push(`description = $${paramIndex++}`)
|
|
251
|
+
params.push(data.description)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (data.listId !== undefined) {
|
|
255
|
+
updates.push(`list_id = $${paramIndex++}`)
|
|
256
|
+
params.push(data.listId)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (data.boardId !== undefined) {
|
|
260
|
+
updates.push(`board_id = $${paramIndex++}`)
|
|
261
|
+
params.push(data.boardId)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (data.position !== undefined) {
|
|
265
|
+
updates.push(`position = $${paramIndex++}`)
|
|
266
|
+
params.push(data.position)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (data.dueDate !== undefined) {
|
|
270
|
+
updates.push(`due_date = $${paramIndex++}`)
|
|
271
|
+
params.push(data.dueDate)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (data.assigneeId !== undefined) {
|
|
275
|
+
updates.push(`assignee_id = $${paramIndex++}`)
|
|
276
|
+
params.push(data.assigneeId)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (data.labels !== undefined) {
|
|
280
|
+
updates.push(`labels = $${paramIndex++}`)
|
|
281
|
+
params.push(JSON.stringify(data.labels))
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (data.isArchived !== undefined) {
|
|
285
|
+
updates.push(`is_archived = $${paramIndex++}`)
|
|
286
|
+
params.push(data.isArchived)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (updates.length === 0) {
|
|
290
|
+
throw new Error('No fields to update')
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
updates.push(`updated_at = NOW()`)
|
|
294
|
+
params.push(id)
|
|
295
|
+
|
|
296
|
+
const result = await mutateWithRLS<DbCard>(
|
|
297
|
+
userId,
|
|
298
|
+
`UPDATE cards SET ${updates.join(', ')} WHERE id = $${paramIndex} RETURNING *`,
|
|
299
|
+
params
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if (!result) {
|
|
303
|
+
throw new Error('Card not found or access denied')
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return mapDbCard(result)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Move a card to a different list with new position
|
|
311
|
+
*/
|
|
312
|
+
static async moveToList(
|
|
313
|
+
cardId: string,
|
|
314
|
+
userId: string,
|
|
315
|
+
newListId: string,
|
|
316
|
+
newPosition: number
|
|
317
|
+
): Promise<Card> {
|
|
318
|
+
return this.update(userId, cardId, {
|
|
319
|
+
listId: newListId,
|
|
320
|
+
position: newPosition,
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Delete a card with RLS
|
|
326
|
+
*/
|
|
327
|
+
static async delete(userId: string, id: string): Promise<boolean> {
|
|
328
|
+
const result = await mutateWithRLS<DbCard>(
|
|
329
|
+
userId,
|
|
330
|
+
`DELETE FROM cards WHERE id = $1 RETURNING *`,
|
|
331
|
+
[id]
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
return result !== null
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card Service Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the CardsService.
|
|
5
|
+
* Cards represent individual tasks or items within a list.
|
|
6
|
+
*
|
|
7
|
+
* @module CardsTypes
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Card label interface
|
|
11
|
+
export interface CardLabel {
|
|
12
|
+
id: string
|
|
13
|
+
name: string
|
|
14
|
+
color: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Main entity interface
|
|
18
|
+
export interface Card {
|
|
19
|
+
id: string
|
|
20
|
+
title: string
|
|
21
|
+
description: string | null
|
|
22
|
+
listId: string
|
|
23
|
+
boardId: string | null
|
|
24
|
+
position: number
|
|
25
|
+
dueDate: string | null
|
|
26
|
+
assigneeId: string | null
|
|
27
|
+
labels: CardLabel[]
|
|
28
|
+
isArchived: boolean
|
|
29
|
+
teamId: string
|
|
30
|
+
userId: string
|
|
31
|
+
createdAt: string
|
|
32
|
+
updatedAt: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// List options
|
|
36
|
+
export interface CardListOptions {
|
|
37
|
+
limit?: number
|
|
38
|
+
offset?: number
|
|
39
|
+
teamId?: string
|
|
40
|
+
listId?: string
|
|
41
|
+
boardId?: string
|
|
42
|
+
assigneeId?: string
|
|
43
|
+
isArchived?: boolean
|
|
44
|
+
orderBy?: 'title' | 'position' | 'dueDate' | 'createdAt'
|
|
45
|
+
orderDir?: 'asc' | 'desc'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// List result
|
|
49
|
+
export interface CardListResult {
|
|
50
|
+
cards: Card[]
|
|
51
|
+
total: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Create data (required fields + teamId + optional fields)
|
|
55
|
+
export interface CardCreateData {
|
|
56
|
+
title: string
|
|
57
|
+
listId: string
|
|
58
|
+
teamId: string
|
|
59
|
+
description?: string
|
|
60
|
+
boardId?: string
|
|
61
|
+
position?: number
|
|
62
|
+
dueDate?: string
|
|
63
|
+
assigneeId?: string
|
|
64
|
+
labels?: CardLabel[]
|
|
65
|
+
isArchived?: boolean
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Update data (all fields optional)
|
|
69
|
+
export interface CardUpdateData {
|
|
70
|
+
title?: string
|
|
71
|
+
description?: string | null
|
|
72
|
+
listId?: string
|
|
73
|
+
boardId?: string | null
|
|
74
|
+
position?: number
|
|
75
|
+
dueDate?: string | null
|
|
76
|
+
assigneeId?: string | null
|
|
77
|
+
labels?: CardLabel[]
|
|
78
|
+
isArchived?: boolean
|
|
79
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Cards",
|
|
3
|
+
"singular": "Card",
|
|
4
|
+
"plural": "Cards",
|
|
5
|
+
"description": "Task cards to track your work",
|
|
6
|
+
|
|
7
|
+
"myCards": "My Cards",
|
|
8
|
+
"allCards": "All Cards",
|
|
9
|
+
"assignedToMe": "Assigned to Me",
|
|
10
|
+
|
|
11
|
+
"fields": {
|
|
12
|
+
"title": {
|
|
13
|
+
"label": "Title",
|
|
14
|
+
"placeholder": "Enter card title...",
|
|
15
|
+
"description": "Card title"
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"label": "Description",
|
|
19
|
+
"placeholder": "Add a more detailed description...",
|
|
20
|
+
"description": "Detailed description of the task"
|
|
21
|
+
},
|
|
22
|
+
"position": {
|
|
23
|
+
"label": "Position",
|
|
24
|
+
"description": "Display order within list"
|
|
25
|
+
},
|
|
26
|
+
"dueDate": {
|
|
27
|
+
"label": "Due Date",
|
|
28
|
+
"placeholder": "Select due date...",
|
|
29
|
+
"description": "When this task is due"
|
|
30
|
+
},
|
|
31
|
+
"labels": {
|
|
32
|
+
"label": "Labels",
|
|
33
|
+
"placeholder": "Select labels...",
|
|
34
|
+
"description": "Tags to categorize this card",
|
|
35
|
+
"options": {
|
|
36
|
+
"urgent": "Urgent",
|
|
37
|
+
"important": "Important",
|
|
38
|
+
"bug": "Bug",
|
|
39
|
+
"feature": "Feature",
|
|
40
|
+
"enhancement": "Enhancement",
|
|
41
|
+
"documentation": "Documentation"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"assigneeId": {
|
|
45
|
+
"label": "Assignee",
|
|
46
|
+
"placeholder": "Assign to...",
|
|
47
|
+
"description": "Team member assigned to this card"
|
|
48
|
+
},
|
|
49
|
+
"listId": {
|
|
50
|
+
"label": "List",
|
|
51
|
+
"placeholder": "Select list...",
|
|
52
|
+
"description": "Parent list"
|
|
53
|
+
},
|
|
54
|
+
"boardId": {
|
|
55
|
+
"label": "Board",
|
|
56
|
+
"placeholder": "Select board...",
|
|
57
|
+
"description": "Parent board"
|
|
58
|
+
},
|
|
59
|
+
"createdAt": {
|
|
60
|
+
"label": "Created At",
|
|
61
|
+
"description": "When the card was created"
|
|
62
|
+
},
|
|
63
|
+
"updatedAt": {
|
|
64
|
+
"label": "Updated At",
|
|
65
|
+
"description": "When the card was last modified"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
"actions": {
|
|
70
|
+
"create": "Add Card",
|
|
71
|
+
"edit": "Edit Card",
|
|
72
|
+
"delete": "Delete Card",
|
|
73
|
+
"move": "Move Card",
|
|
74
|
+
"assign": "Assign",
|
|
75
|
+
"unassign": "Unassign",
|
|
76
|
+
"addLabel": "Add Label",
|
|
77
|
+
"removeLabel": "Remove Label",
|
|
78
|
+
"setDueDate": "Set Due Date",
|
|
79
|
+
"removeDueDate": "Remove Due Date"
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
"messages": {
|
|
83
|
+
"created": "Card created successfully",
|
|
84
|
+
"updated": "Card updated successfully",
|
|
85
|
+
"deleted": "Card deleted successfully",
|
|
86
|
+
"moved": "Card moved to {listName}",
|
|
87
|
+
"assigned": "Card assigned to {userName}",
|
|
88
|
+
"unassigned": "Card unassigned",
|
|
89
|
+
"confirmDelete": "Are you sure you want to delete this card?"
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
"empty": {
|
|
93
|
+
"title": "No cards in this list",
|
|
94
|
+
"description": "Add a card to get started.",
|
|
95
|
+
"action": "Add a card"
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
"filters": {
|
|
99
|
+
"all": "All Cards",
|
|
100
|
+
"assignedToMe": "Assigned to Me",
|
|
101
|
+
"unassigned": "Unassigned",
|
|
102
|
+
"overdue": "Overdue",
|
|
103
|
+
"dueThisWeek": "Due This Week",
|
|
104
|
+
"noDueDate": "No Due Date"
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
"dueStatus": {
|
|
108
|
+
"overdue": "Overdue",
|
|
109
|
+
"dueToday": "Due Today",
|
|
110
|
+
"dueTomorrow": "Due Tomorrow",
|
|
111
|
+
"dueThisWeek": "Due This Week"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Tarjetas",
|
|
3
|
+
"singular": "Tarjeta",
|
|
4
|
+
"plural": "Tarjetas",
|
|
5
|
+
"description": "Tarjetas de tareas para seguir tu trabajo",
|
|
6
|
+
|
|
7
|
+
"myCards": "Mis Tarjetas",
|
|
8
|
+
"allCards": "Todas las Tarjetas",
|
|
9
|
+
"assignedToMe": "Asignadas a Mí",
|
|
10
|
+
|
|
11
|
+
"fields": {
|
|
12
|
+
"title": {
|
|
13
|
+
"label": "Título",
|
|
14
|
+
"placeholder": "Ingresa el título de la tarjeta...",
|
|
15
|
+
"description": "Título de la tarjeta"
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"label": "Descripción",
|
|
19
|
+
"placeholder": "Agrega una descripción más detallada...",
|
|
20
|
+
"description": "Descripción detallada de la tarea"
|
|
21
|
+
},
|
|
22
|
+
"position": {
|
|
23
|
+
"label": "Posición",
|
|
24
|
+
"description": "Orden de visualización dentro de la lista"
|
|
25
|
+
},
|
|
26
|
+
"dueDate": {
|
|
27
|
+
"label": "Fecha Límite",
|
|
28
|
+
"placeholder": "Seleccionar fecha límite...",
|
|
29
|
+
"description": "Cuándo vence esta tarea"
|
|
30
|
+
},
|
|
31
|
+
"labels": {
|
|
32
|
+
"label": "Etiquetas",
|
|
33
|
+
"placeholder": "Seleccionar etiquetas...",
|
|
34
|
+
"description": "Tags para categorizar esta tarjeta",
|
|
35
|
+
"options": {
|
|
36
|
+
"urgent": "Urgente",
|
|
37
|
+
"important": "Importante",
|
|
38
|
+
"bug": "Bug",
|
|
39
|
+
"feature": "Funcionalidad",
|
|
40
|
+
"enhancement": "Mejora",
|
|
41
|
+
"documentation": "Documentación"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"assigneeId": {
|
|
45
|
+
"label": "Asignado a",
|
|
46
|
+
"placeholder": "Asignar a...",
|
|
47
|
+
"description": "Miembro del equipo asignado a esta tarjeta"
|
|
48
|
+
},
|
|
49
|
+
"listId": {
|
|
50
|
+
"label": "Lista",
|
|
51
|
+
"placeholder": "Seleccionar lista...",
|
|
52
|
+
"description": "Lista padre"
|
|
53
|
+
},
|
|
54
|
+
"boardId": {
|
|
55
|
+
"label": "Tablero",
|
|
56
|
+
"placeholder": "Seleccionar tablero...",
|
|
57
|
+
"description": "Tablero padre"
|
|
58
|
+
},
|
|
59
|
+
"createdAt": {
|
|
60
|
+
"label": "Creado",
|
|
61
|
+
"description": "Cuándo se creó la tarjeta"
|
|
62
|
+
},
|
|
63
|
+
"updatedAt": {
|
|
64
|
+
"label": "Actualizado",
|
|
65
|
+
"description": "Cuándo se modificó por última vez"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
"actions": {
|
|
70
|
+
"create": "Agregar Tarjeta",
|
|
71
|
+
"edit": "Editar Tarjeta",
|
|
72
|
+
"delete": "Eliminar Tarjeta",
|
|
73
|
+
"move": "Mover Tarjeta",
|
|
74
|
+
"assign": "Asignar",
|
|
75
|
+
"unassign": "Desasignar",
|
|
76
|
+
"addLabel": "Agregar Etiqueta",
|
|
77
|
+
"removeLabel": "Quitar Etiqueta",
|
|
78
|
+
"setDueDate": "Establecer Fecha Límite",
|
|
79
|
+
"removeDueDate": "Quitar Fecha Límite"
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
"messages": {
|
|
83
|
+
"created": "Tarjeta creada exitosamente",
|
|
84
|
+
"updated": "Tarjeta actualizada exitosamente",
|
|
85
|
+
"deleted": "Tarjeta eliminada exitosamente",
|
|
86
|
+
"moved": "Tarjeta movida a {listName}",
|
|
87
|
+
"assigned": "Tarjeta asignada a {userName}",
|
|
88
|
+
"unassigned": "Tarjeta desasignada",
|
|
89
|
+
"confirmDelete": "¿Estás seguro de que quieres eliminar esta tarjeta?"
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
"empty": {
|
|
93
|
+
"title": "No hay tarjetas en esta lista",
|
|
94
|
+
"description": "Agrega una tarjeta para comenzar.",
|
|
95
|
+
"action": "Agregar una tarjeta"
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
"filters": {
|
|
99
|
+
"all": "Todas las Tarjetas",
|
|
100
|
+
"assignedToMe": "Asignadas a Mí",
|
|
101
|
+
"unassigned": "Sin Asignar",
|
|
102
|
+
"overdue": "Vencidas",
|
|
103
|
+
"dueThisWeek": "Vencen Esta Semana",
|
|
104
|
+
"noDueDate": "Sin Fecha Límite"
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
"dueStatus": {
|
|
108
|
+
"overdue": "Vencida",
|
|
109
|
+
"dueToday": "Vence Hoy",
|
|
110
|
+
"dueTomorrow": "Vence Mañana",
|
|
111
|
+
"dueThisWeek": "Vence Esta Semana"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|