@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.
Files changed (57) hide show
  1. package/README.md +76 -0
  2. package/about.md +123 -0
  3. package/components/CardDetailModal.tsx +318 -0
  4. package/components/KanbanBoard.tsx +612 -0
  5. package/components/KanbanCard.tsx +218 -0
  6. package/components/KanbanColumn.tsx +264 -0
  7. package/components/SortableList.tsx +46 -0
  8. package/components/index.ts +4 -0
  9. package/config/app.config.ts +172 -0
  10. package/config/billing.config.ts +187 -0
  11. package/config/dashboard.config.ts +357 -0
  12. package/config/dev.config.ts +55 -0
  13. package/config/features.config.ts +256 -0
  14. package/config/flows.config.ts +484 -0
  15. package/config/permissions.config.ts +167 -0
  16. package/config/theme.config.ts +106 -0
  17. package/entities/boards/boards.config.ts +61 -0
  18. package/entities/boards/boards.fields.ts +154 -0
  19. package/entities/boards/boards.service.ts +256 -0
  20. package/entities/boards/boards.types.ts +57 -0
  21. package/entities/boards/messages/en.json +80 -0
  22. package/entities/boards/messages/es.json +80 -0
  23. package/entities/boards/migrations/001_boards_table.sql +83 -0
  24. package/entities/cards/cards.config.ts +61 -0
  25. package/entities/cards/cards.fields.ts +242 -0
  26. package/entities/cards/cards.service.ts +336 -0
  27. package/entities/cards/cards.types.ts +79 -0
  28. package/entities/cards/messages/en.json +114 -0
  29. package/entities/cards/messages/es.json +114 -0
  30. package/entities/cards/migrations/020_cards_table.sql +92 -0
  31. package/entities/lists/lists.config.ts +61 -0
  32. package/entities/lists/lists.fields.ts +105 -0
  33. package/entities/lists/lists.service.ts +252 -0
  34. package/entities/lists/lists.types.ts +55 -0
  35. package/entities/lists/messages/en.json +60 -0
  36. package/entities/lists/messages/es.json +60 -0
  37. package/entities/lists/migrations/010_lists_table.sql +79 -0
  38. package/lib/selectors.ts +206 -0
  39. package/messages/en.json +79 -0
  40. package/messages/es.json +79 -0
  41. package/migrations/999_theme_sample_data.sql +922 -0
  42. package/migrations/999a_initial_sample_data.sql +377 -0
  43. package/migrations/999b_abundant_sample_data.sql +346 -0
  44. package/package.json +17 -0
  45. package/permissions-matrix.md +122 -0
  46. package/styles/components.css +460 -0
  47. package/styles/globals.css +560 -0
  48. package/templates/dashboard/(main)/boards/[id]/[cardId]/page.tsx +238 -0
  49. package/templates/dashboard/(main)/boards/[id]/edit/page.tsx +390 -0
  50. package/templates/dashboard/(main)/boards/[id]/page.tsx +236 -0
  51. package/templates/dashboard/(main)/boards/create/page.tsx +236 -0
  52. package/templates/dashboard/(main)/boards/page.tsx +335 -0
  53. package/templates/dashboard/(main)/layout.tsx +32 -0
  54. package/templates/dashboard/(main)/page.tsx +592 -0
  55. package/templates/shared/ProductivityMobileNav.tsx +410 -0
  56. package/templates/shared/ProductivitySidebar.tsx +538 -0
  57. package/templates/shared/ProductivityTopBar.tsx +317 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Productivity Theme Configuration
3
+ *
4
+ * A Trello-style task management app with boards, lists, and cards.
5
+ * Collaborative mode: owner can invite team members.
6
+ */
7
+
8
+ import type { ThemeConfig } from '@nextsparkjs/core/types/theme'
9
+
10
+ export const productivityThemeConfig: ThemeConfig = {
11
+ name: 'productivity',
12
+ displayName: 'Productivity',
13
+ version: '1.0.0',
14
+ description: 'A collaborative task management app with boards, lists, and cards',
15
+ author: 'NextSpark Team',
16
+
17
+ plugins: [],
18
+
19
+ styles: {
20
+ globals: 'globals.css',
21
+ components: 'components.css',
22
+ variables: {
23
+ '--spacing-xs': '0.125rem',
24
+ '--spacing-sm': '0.25rem',
25
+ '--spacing-md': '0.5rem',
26
+ '--spacing-lg': '1rem',
27
+ '--spacing-xl': '1.5rem',
28
+ '--spacing-2xl': '2rem'
29
+ }
30
+ },
31
+
32
+ // Modern, clean productivity aesthetic
33
+ config: {
34
+ colors: {
35
+ background: 'oklch(0.98 0.005 240)',
36
+ foreground: 'oklch(0.2 0.02 260)',
37
+ card: 'oklch(1.0 0 0)',
38
+ 'card-foreground': 'oklch(0.2 0.02 260)',
39
+ popover: 'oklch(1.0 0 0)',
40
+ 'popover-foreground': 'oklch(0.2 0.02 260)',
41
+ // Blue primary for productivity focus
42
+ primary: 'oklch(0.55 0.2 250)',
43
+ 'primary-foreground': 'oklch(1.0 0 0)',
44
+ secondary: 'oklch(0.95 0.01 240)',
45
+ 'secondary-foreground': 'oklch(0.3 0.02 260)',
46
+ muted: 'oklch(0.96 0.005 240)',
47
+ 'muted-foreground': 'oklch(0.45 0.015 260)',
48
+ accent: 'oklch(0.92 0.03 200)',
49
+ 'accent-foreground': 'oklch(0.2 0.02 260)',
50
+ destructive: 'oklch(0.55 0.22 25)',
51
+ 'destructive-foreground': 'oklch(1.0 0 0)',
52
+ border: 'oklch(0.9 0.01 240)',
53
+ input: 'oklch(0.9 0.01 240)',
54
+ ring: 'oklch(0.55 0.2 250)',
55
+
56
+ // Chart colors for analytics
57
+ 'chart-1': 'oklch(0.55 0.2 250)',
58
+ 'chart-2': 'oklch(0.6 0.18 150)',
59
+ 'chart-3': 'oklch(0.65 0.15 50)',
60
+ 'chart-4': 'oklch(0.5 0.2 300)',
61
+ 'chart-5': 'oklch(0.55 0.15 30)',
62
+
63
+ // Sidebar - slightly tinted
64
+ sidebar: 'oklch(0.97 0.01 250)',
65
+ 'sidebar-foreground': 'oklch(0.2 0.02 260)',
66
+ 'sidebar-primary': 'oklch(0.55 0.2 250)',
67
+ 'sidebar-primary-foreground': 'oklch(1.0 0 0)',
68
+ 'sidebar-accent': 'oklch(0.92 0.03 200)',
69
+ 'sidebar-accent-foreground': 'oklch(0.2 0.02 260)',
70
+ 'sidebar-border': 'oklch(0.9 0.01 240)',
71
+ 'sidebar-ring': 'oklch(0.55 0.2 250)'
72
+ },
73
+
74
+ fonts: {
75
+ sans: 'Inter, system-ui, sans-serif',
76
+ serif: 'Georgia, serif',
77
+ mono: 'JetBrains Mono, monospace'
78
+ },
79
+
80
+ spacing: {
81
+ radius: '0.75rem',
82
+ 'radius-sm': '0.5rem',
83
+ 'radius-md': '0.625rem',
84
+ 'radius-lg': '0.75rem',
85
+ 'radius-xl': '1rem'
86
+ },
87
+
88
+ breakpoints: {
89
+ 'shadow-2xs': '0 1px 2px 0 rgb(0 0 0 / 0.03)',
90
+ 'shadow-xs': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
91
+ 'shadow-sm': '0 1px 3px 0 rgb(0 0 0 / 0.1)',
92
+ shadow: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
93
+ 'shadow-md': '0 4px 6px -1px rgb(0 0 0 / 0.1)',
94
+ 'shadow-lg': '0 10px 15px -3px rgb(0 0 0 / 0.1)',
95
+ 'shadow-xl': '0 20px 25px -5px rgb(0 0 0 / 0.1)',
96
+ 'shadow-2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)'
97
+ }
98
+ },
99
+
100
+ components: {
101
+ overrides: {},
102
+ custom: {}
103
+ }
104
+ }
105
+
106
+ export default productivityThemeConfig
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Boards Entity Configuration
3
+ *
4
+ * Trello-style boards for organizing lists and cards.
5
+ */
6
+
7
+ import { LayoutDashboard } from 'lucide-react'
8
+ import type { EntityConfig } from '@nextsparkjs/core/lib/entities/types'
9
+ import { boardFields } from './boards.fields'
10
+
11
+ export const boardEntityConfig: EntityConfig = {
12
+ // ==========================================
13
+ // 1. BASIC IDENTIFICATION
14
+ // ==========================================
15
+ slug: 'boards',
16
+ enabled: true,
17
+ names: {
18
+ singular: 'board',
19
+ plural: 'Boards'
20
+ },
21
+ icon: LayoutDashboard,
22
+
23
+ // ==========================================
24
+ // 2. ACCESS AND SCOPE CONFIGURATION
25
+ // ==========================================
26
+ access: {
27
+ public: false, // Private boards
28
+ api: true,
29
+ metadata: false,
30
+ shared: true // Shared within team
31
+ },
32
+
33
+ // ==========================================
34
+ // 3. UI/UX FEATURES
35
+ // ==========================================
36
+ ui: {
37
+ dashboard: {
38
+ showInMenu: true,
39
+ showInTopbar: true
40
+ },
41
+ public: {
42
+ hasArchivePage: false,
43
+ hasSinglePage: false
44
+ },
45
+ features: {
46
+ searchable: true,
47
+ sortable: true,
48
+ filterable: true,
49
+ bulkOperations: false,
50
+ importExport: false
51
+ }
52
+ },
53
+
54
+ // ==========================================
55
+ // FIELDS
56
+ // ==========================================
57
+ fields: boardFields,
58
+ }
59
+
60
+ export default boardEntityConfig
61
+
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Boards Entity Fields Configuration
3
+ */
4
+
5
+ import type { EntityField } from '@nextsparkjs/core/lib/entities/types'
6
+
7
+ export const boardFields: EntityField[] = [
8
+ {
9
+ name: 'name',
10
+ type: 'text',
11
+ required: true,
12
+ display: {
13
+ label: 'Name',
14
+ description: 'Board name',
15
+ placeholder: 'Enter board name...',
16
+ showInList: true,
17
+ showInDetail: true,
18
+ showInForm: true,
19
+ order: 1,
20
+ columnWidth: 12,
21
+ },
22
+ api: {
23
+ readOnly: false,
24
+ searchable: true,
25
+ sortable: true,
26
+ },
27
+ },
28
+ {
29
+ name: 'description',
30
+ type: 'textarea',
31
+ required: false,
32
+ display: {
33
+ label: 'Description',
34
+ description: 'Optional board description',
35
+ placeholder: 'Describe what this board is for...',
36
+ showInList: false,
37
+ showInDetail: true,
38
+ showInForm: true,
39
+ order: 2,
40
+ columnWidth: 12,
41
+ },
42
+ api: {
43
+ readOnly: false,
44
+ searchable: true,
45
+ sortable: false,
46
+ },
47
+ },
48
+ {
49
+ name: 'color',
50
+ type: 'select',
51
+ required: false,
52
+ defaultValue: 'blue',
53
+ options: [
54
+ { value: 'blue', label: 'Blue' },
55
+ { value: 'green', label: 'Green' },
56
+ { value: 'purple', label: 'Purple' },
57
+ { value: 'orange', label: 'Orange' },
58
+ { value: 'red', label: 'Red' },
59
+ { value: 'pink', label: 'Pink' },
60
+ { value: 'gray', label: 'Gray' },
61
+ ],
62
+ display: {
63
+ label: 'Color',
64
+ description: 'Board background color',
65
+ placeholder: 'Select color...',
66
+ showInList: true,
67
+ showInDetail: true,
68
+ showInForm: true,
69
+ order: 3,
70
+ columnWidth: 6,
71
+ },
72
+ api: {
73
+ readOnly: false,
74
+ searchable: false,
75
+ sortable: false,
76
+ },
77
+ },
78
+ {
79
+ name: 'archived',
80
+ type: 'boolean',
81
+ required: false,
82
+ defaultValue: false,
83
+ display: {
84
+ label: 'Archived',
85
+ description: 'Archive this board',
86
+ showInList: false,
87
+ showInDetail: true,
88
+ showInForm: true,
89
+ order: 4,
90
+ columnWidth: 6,
91
+ },
92
+ api: {
93
+ readOnly: false,
94
+ searchable: false,
95
+ sortable: true,
96
+ },
97
+ },
98
+ {
99
+ name: 'position',
100
+ type: 'number',
101
+ required: false,
102
+ defaultValue: 0,
103
+ display: {
104
+ label: 'Position',
105
+ description: 'Display order',
106
+ showInList: false,
107
+ showInDetail: false,
108
+ showInForm: false,
109
+ order: 5,
110
+ },
111
+ api: {
112
+ readOnly: false,
113
+ searchable: false,
114
+ sortable: true,
115
+ },
116
+ },
117
+ {
118
+ name: 'createdAt',
119
+ type: 'datetime',
120
+ required: false,
121
+ display: {
122
+ label: 'Created At',
123
+ description: 'When the board was created',
124
+ showInList: true,
125
+ showInDetail: true,
126
+ showInForm: false,
127
+ order: 98,
128
+ },
129
+ api: {
130
+ readOnly: true,
131
+ searchable: false,
132
+ sortable: true,
133
+ },
134
+ },
135
+ {
136
+ name: 'updatedAt',
137
+ type: 'datetime',
138
+ required: false,
139
+ display: {
140
+ label: 'Updated At',
141
+ description: 'When the board was last modified',
142
+ showInList: false,
143
+ showInDetail: true,
144
+ showInForm: false,
145
+ order: 99,
146
+ },
147
+ api: {
148
+ readOnly: true,
149
+ searchable: false,
150
+ sortable: true,
151
+ },
152
+ },
153
+ ]
154
+
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Boards Service
3
+ * Provides data access methods for boards entity.
4
+ */
5
+ import { queryOneWithRLS, queryWithRLS, mutateWithRLS } from '@nextsparkjs/core/lib/db'
6
+
7
+ export interface Board {
8
+ id: string
9
+ name: string
10
+ description: string | null
11
+ color: string | null
12
+ isArchived: boolean
13
+ position: number
14
+ userId: string
15
+ teamId: string
16
+ createdAt: Date
17
+ updatedAt: Date
18
+ }
19
+
20
+ export interface BoardListOptions {
21
+ limit?: number
22
+ offset?: number
23
+ orderBy?: string
24
+ orderDir?: 'asc' | 'desc'
25
+ teamId?: string
26
+ isArchived?: boolean
27
+ }
28
+
29
+ export interface BoardListResult {
30
+ boards: Board[]
31
+ total: number
32
+ }
33
+
34
+ export interface BoardCreateData {
35
+ name: string
36
+ description?: string | null
37
+ color?: string | null
38
+ isArchived?: boolean
39
+ position?: number
40
+ teamId: string
41
+ }
42
+
43
+ export interface BoardUpdateData {
44
+ name?: string
45
+ description?: string | null
46
+ color?: string | null
47
+ isArchived?: boolean
48
+ position?: number
49
+ }
50
+
51
+ interface DbBoard {
52
+ id: string
53
+ name: string
54
+ description: string | null
55
+ color: string | null
56
+ is_archived: boolean
57
+ position: number
58
+ user_id: string
59
+ team_id: string
60
+ created_at: string
61
+ updated_at: string
62
+ }
63
+
64
+ function mapDbBoard(dbBoard: DbBoard): Board {
65
+ return {
66
+ id: dbBoard.id,
67
+ name: dbBoard.name,
68
+ description: dbBoard.description,
69
+ color: dbBoard.color,
70
+ isArchived: dbBoard.is_archived,
71
+ position: dbBoard.position,
72
+ userId: dbBoard.user_id,
73
+ teamId: dbBoard.team_id,
74
+ createdAt: new Date(dbBoard.created_at),
75
+ updatedAt: new Date(dbBoard.updated_at),
76
+ }
77
+ }
78
+
79
+ export class BoardsService {
80
+ /**
81
+ * Get a board by ID with RLS
82
+ */
83
+ static async getById(id: string, userId: string): Promise<Board | null> {
84
+ const result = await queryOneWithRLS<DbBoard>(
85
+ userId,
86
+ `SELECT * FROM boards WHERE id = $1`,
87
+ [id]
88
+ )
89
+
90
+ return result ? mapDbBoard(result) : null
91
+ }
92
+
93
+ /**
94
+ * List boards with RLS and filtering
95
+ */
96
+ static async list(userId: string, options: BoardListOptions = {}): Promise<BoardListResult> {
97
+ const {
98
+ limit = 50,
99
+ offset = 0,
100
+ orderBy = 'position',
101
+ orderDir = 'asc',
102
+ teamId,
103
+ isArchived,
104
+ } = options
105
+
106
+ const conditions: string[] = []
107
+ const params: any[] = []
108
+ let paramIndex = 1
109
+
110
+ if (teamId) {
111
+ conditions.push(`team_id = $${paramIndex++}`)
112
+ params.push(teamId)
113
+ }
114
+
115
+ if (isArchived !== undefined) {
116
+ conditions.push(`is_archived = $${paramIndex++}`)
117
+ params.push(isArchived)
118
+ }
119
+
120
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
121
+
122
+ // Get total count
123
+ const countResult = await queryOneWithRLS<{ count: string }>(
124
+ userId,
125
+ `SELECT COUNT(*) as count FROM boards ${whereClause}`,
126
+ params
127
+ )
128
+ const total = parseInt(countResult?.count || '0', 10)
129
+
130
+ // Get boards
131
+ const validOrderBy = ['position', 'name', 'created_at', 'updated_at'].includes(orderBy)
132
+ ? orderBy
133
+ : 'position'
134
+ const validOrderDir = orderDir === 'desc' ? 'DESC' : 'ASC'
135
+
136
+ params.push(limit, offset)
137
+ const boards = await queryWithRLS<DbBoard>(
138
+ userId,
139
+ `SELECT * FROM boards ${whereClause} ORDER BY ${validOrderBy} ${validOrderDir} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
140
+ params
141
+ )
142
+
143
+ return {
144
+ boards: boards.map(mapDbBoard),
145
+ total,
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Get boards ordered by position
151
+ */
152
+ static async getByPosition(userId: string, teamId?: string): Promise<Board[]> {
153
+ const result = await this.list(userId, {
154
+ orderBy: 'position',
155
+ orderDir: 'asc',
156
+ teamId,
157
+ isArchived: false,
158
+ })
159
+
160
+ return result.boards
161
+ }
162
+
163
+ /**
164
+ * Create a new board with RLS
165
+ */
166
+ static async create(userId: string, data: BoardCreateData): Promise<Board> {
167
+ const {
168
+ name,
169
+ description = null,
170
+ color = null,
171
+ isArchived = false,
172
+ position = 0,
173
+ teamId,
174
+ } = data
175
+
176
+ const result = await mutateWithRLS<DbBoard>(
177
+ userId,
178
+ `INSERT INTO boards (name, description, color, is_archived, position, user_id, team_id)
179
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
180
+ RETURNING *`,
181
+ [name, description, color, isArchived, position, userId, teamId]
182
+ )
183
+
184
+ if (!result) {
185
+ throw new Error('Failed to create board')
186
+ }
187
+
188
+ return mapDbBoard(result)
189
+ }
190
+
191
+ /**
192
+ * Update a board with RLS
193
+ */
194
+ static async update(userId: string, id: string, data: BoardUpdateData): Promise<Board> {
195
+ const updates: string[] = []
196
+ const params: any[] = []
197
+ let paramIndex = 1
198
+
199
+ if (data.name !== undefined) {
200
+ updates.push(`name = $${paramIndex++}`)
201
+ params.push(data.name)
202
+ }
203
+
204
+ if (data.description !== undefined) {
205
+ updates.push(`description = $${paramIndex++}`)
206
+ params.push(data.description)
207
+ }
208
+
209
+ if (data.color !== undefined) {
210
+ updates.push(`color = $${paramIndex++}`)
211
+ params.push(data.color)
212
+ }
213
+
214
+ if (data.isArchived !== undefined) {
215
+ updates.push(`is_archived = $${paramIndex++}`)
216
+ params.push(data.isArchived)
217
+ }
218
+
219
+ if (data.position !== undefined) {
220
+ updates.push(`position = $${paramIndex++}`)
221
+ params.push(data.position)
222
+ }
223
+
224
+ if (updates.length === 0) {
225
+ throw new Error('No fields to update')
226
+ }
227
+
228
+ updates.push(`updated_at = NOW()`)
229
+ params.push(id)
230
+
231
+ const result = await mutateWithRLS<DbBoard>(
232
+ userId,
233
+ `UPDATE boards SET ${updates.join(', ')} WHERE id = $${paramIndex} RETURNING *`,
234
+ params
235
+ )
236
+
237
+ if (!result) {
238
+ throw new Error('Board not found or access denied')
239
+ }
240
+
241
+ return mapDbBoard(result)
242
+ }
243
+
244
+ /**
245
+ * Delete a board with RLS
246
+ */
247
+ static async delete(userId: string, id: string): Promise<boolean> {
248
+ const result = await mutateWithRLS<DbBoard>(
249
+ userId,
250
+ `DELETE FROM boards WHERE id = $1 RETURNING *`,
251
+ [id]
252
+ )
253
+
254
+ return result !== null
255
+ }
256
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Board Service Types
3
+ *
4
+ * Type definitions for the BoardsService.
5
+ * Boards represent workspaces that contain lists and cards for project organization.
6
+ *
7
+ * @module BoardsTypes
8
+ */
9
+
10
+ // Main entity interface
11
+ export interface Board {
12
+ id: string
13
+ name: string
14
+ description: string | null
15
+ color: string | null
16
+ isArchived: boolean
17
+ position: number
18
+ teamId: string
19
+ userId: string
20
+ createdAt: string
21
+ updatedAt: string
22
+ }
23
+
24
+ // List options
25
+ export interface BoardListOptions {
26
+ limit?: number
27
+ offset?: number
28
+ teamId?: string
29
+ isArchived?: boolean
30
+ orderBy?: 'name' | 'position' | 'createdAt'
31
+ orderDir?: 'asc' | 'desc'
32
+ }
33
+
34
+ // List result
35
+ export interface BoardListResult {
36
+ boards: Board[]
37
+ total: number
38
+ }
39
+
40
+ // Create data (required fields + teamId + optional fields)
41
+ export interface BoardCreateData {
42
+ name: string
43
+ teamId: string
44
+ description?: string
45
+ color?: string
46
+ isArchived?: boolean
47
+ position?: number
48
+ }
49
+
50
+ // Update data (all fields optional)
51
+ export interface BoardUpdateData {
52
+ name?: string
53
+ description?: string | null
54
+ color?: string | null
55
+ isArchived?: boolean
56
+ position?: number
57
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "title": "Boards",
3
+ "singular": "Board",
4
+ "plural": "Boards",
5
+ "description": "Organize your work into boards",
6
+
7
+ "fields": {
8
+ "name": {
9
+ "label": "Name",
10
+ "placeholder": "Enter board name...",
11
+ "description": "Board name"
12
+ },
13
+ "description": {
14
+ "label": "Description",
15
+ "placeholder": "Describe what this board is for...",
16
+ "description": "Optional board description"
17
+ },
18
+ "color": {
19
+ "label": "Color",
20
+ "placeholder": "Select color...",
21
+ "description": "Board background color",
22
+ "options": {
23
+ "blue": "Blue",
24
+ "green": "Green",
25
+ "purple": "Purple",
26
+ "orange": "Orange",
27
+ "red": "Red",
28
+ "pink": "Pink",
29
+ "gray": "Gray"
30
+ }
31
+ },
32
+ "archived": {
33
+ "label": "Archived",
34
+ "description": "Archive this board"
35
+ },
36
+ "position": {
37
+ "label": "Position",
38
+ "description": "Display order"
39
+ },
40
+ "createdAt": {
41
+ "label": "Created At",
42
+ "description": "When the board was created"
43
+ },
44
+ "updatedAt": {
45
+ "label": "Updated At",
46
+ "description": "When the board was last modified"
47
+ }
48
+ },
49
+
50
+ "actions": {
51
+ "create": "New Board",
52
+ "edit": "Edit Board",
53
+ "delete": "Delete Board",
54
+ "archive": "Archive Board",
55
+ "unarchive": "Unarchive Board",
56
+ "duplicate": "Duplicate Board"
57
+ },
58
+
59
+ "messages": {
60
+ "created": "Board created successfully",
61
+ "updated": "Board updated successfully",
62
+ "deleted": "Board deleted successfully",
63
+ "archived": "Board archived",
64
+ "unarchived": "Board unarchived",
65
+ "confirmDelete": "Are you sure you want to delete this board? All lists and cards will be permanently removed."
66
+ },
67
+
68
+ "empty": {
69
+ "title": "No boards yet",
70
+ "description": "Create your first board to start organizing your work.",
71
+ "action": "Create your first board"
72
+ },
73
+
74
+ "filters": {
75
+ "all": "All Boards",
76
+ "active": "Active",
77
+ "archived": "Archived"
78
+ }
79
+ }
80
+