@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,79 @@
1
+ -- ============================================================================
2
+ -- Lists Table Migration
3
+ -- Productivity theme: Columns within boards
4
+ -- ============================================================================
5
+
6
+ CREATE TABLE IF NOT EXISTS "lists" (
7
+ "id" TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
8
+ "name" VARCHAR(255) NOT NULL,
9
+ "position" INTEGER DEFAULT 0,
10
+ "boardId" TEXT NOT NULL REFERENCES "boards"("id") ON DELETE CASCADE,
11
+ "userId" TEXT NOT NULL REFERENCES "users"("id") ON DELETE CASCADE,
12
+ "teamId" TEXT NOT NULL REFERENCES "teams"("id") ON DELETE CASCADE,
13
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
14
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()
15
+ );
16
+
17
+ -- Indexes
18
+ CREATE INDEX IF NOT EXISTS "lists_boardId_idx" ON "lists" ("boardId");
19
+ CREATE INDEX IF NOT EXISTS "lists_teamId_idx" ON "lists" ("teamId");
20
+ CREATE INDEX IF NOT EXISTS "lists_position_idx" ON "lists" ("boardId", "position");
21
+
22
+ -- Enable RLS
23
+ ALTER TABLE "lists" ENABLE ROW LEVEL SECURITY;
24
+
25
+ -- Drop existing policies
26
+ DROP POLICY IF EXISTS "lists_select_policy" ON "lists";
27
+ DROP POLICY IF EXISTS "lists_insert_policy" ON "lists";
28
+ DROP POLICY IF EXISTS "lists_update_policy" ON "lists";
29
+ DROP POLICY IF EXISTS "lists_delete_policy" ON "lists";
30
+
31
+ -- Policy: Team members can view lists
32
+ CREATE POLICY "lists_select_policy" ON "lists"
33
+ FOR SELECT TO authenticated
34
+ USING (
35
+ "teamId" = ANY(public.get_user_team_ids())
36
+ OR public.is_superadmin()
37
+ );
38
+
39
+ -- Policy: Team members can create lists
40
+ CREATE POLICY "lists_insert_policy" ON "lists"
41
+ FOR INSERT TO authenticated
42
+ WITH CHECK (
43
+ "teamId" = ANY(public.get_user_team_ids())
44
+ );
45
+
46
+ -- Policy: Team members can update lists
47
+ CREATE POLICY "lists_update_policy" ON "lists"
48
+ FOR UPDATE TO authenticated
49
+ USING (
50
+ "teamId" = ANY(public.get_user_team_ids())
51
+ OR public.is_superadmin()
52
+ );
53
+
54
+ -- Policy: Team members can delete lists (owner only at app level)
55
+ CREATE POLICY "lists_delete_policy" ON "lists"
56
+ FOR DELETE TO authenticated
57
+ USING (
58
+ "teamId" = ANY(public.get_user_team_ids())
59
+ OR public.is_superadmin()
60
+ );
61
+
62
+ -- Trigger for updatedAt
63
+ CREATE OR REPLACE FUNCTION update_lists_updated_at()
64
+ RETURNS TRIGGER AS $$
65
+ BEGIN
66
+ NEW."updatedAt" = NOW();
67
+ RETURN NEW;
68
+ END;
69
+ $$ LANGUAGE plpgsql;
70
+
71
+ DROP TRIGGER IF EXISTS lists_updated_at_trigger ON "lists";
72
+ CREATE TRIGGER lists_updated_at_trigger
73
+ BEFORE UPDATE ON "lists"
74
+ FOR EACH ROW
75
+ EXECUTE FUNCTION update_lists_updated_at();
76
+
77
+ -- Comments
78
+ COMMENT ON TABLE "lists" IS 'Columns within boards (To Do, In Progress, Done, etc.)';
79
+ COMMENT ON COLUMN "lists"."position" IS 'Display order within the parent board';
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Productivity Theme - Block Selectors
3
+ *
4
+ * This file defines selectors for block components in the theme.
5
+ * It's placed in lib/ instead of tests/ so TypeScript can resolve imports.
6
+ *
7
+ * Used by:
8
+ * - Block components (for data-cy attributes)
9
+ * - Cypress tests (via tests/cypress/src/selectors.ts)
10
+ */
11
+
12
+ import { createSelectorHelpers } from '@nextsparkjs/core/lib/test/selector-factory'
13
+ import { CORE_SELECTORS } from '@nextsparkjs/core/lib/test/core-selectors'
14
+
15
+ // =============================================================================
16
+ // BLOCK SELECTORS
17
+ // =============================================================================
18
+
19
+ /**
20
+ * Block-specific selectors for the productivity theme.
21
+ * Each block has at minimum a 'container' selector.
22
+ * Dynamic selectors use {index} placeholder.
23
+ */
24
+ export const BLOCK_SELECTORS = {
25
+ // Productivity theme currently has no custom blocks
26
+ // Add block selectors here when blocks are created
27
+ } as const
28
+
29
+ // =============================================================================
30
+ // ENTITY SELECTORS
31
+ // =============================================================================
32
+
33
+ /**
34
+ * Entity-specific selectors for the productivity theme.
35
+ */
36
+ export const ENTITY_SELECTORS = {
37
+ boards: {
38
+ list: 'boards-list',
39
+ listItem: 'board-item-{index}',
40
+ card: 'board-card-{id}',
41
+ name: 'board-name',
42
+ description: 'board-description',
43
+ createButton: 'board-create-button',
44
+ editButton: 'board-edit-button',
45
+ deleteButton: 'board-delete-button',
46
+ settingsButton: 'board-settings-button',
47
+ form: {
48
+ container: 'board-form',
49
+ name: 'board-form-name',
50
+ description: 'board-form-description',
51
+ submit: 'board-form-submit',
52
+ cancel: 'board-form-cancel',
53
+ },
54
+ },
55
+ lists: {
56
+ container: 'list-container-{id}',
57
+ header: 'list-header-{id}',
58
+ name: 'list-name',
59
+ cardsContainer: 'list-cards-{id}',
60
+ addCardButton: 'list-add-card-{id}',
61
+ createButton: 'list-create-button',
62
+ editButton: 'list-edit-button',
63
+ deleteButton: 'list-delete-button',
64
+ moveHandle: 'list-move-handle-{id}',
65
+ form: {
66
+ container: 'list-form',
67
+ name: 'list-form-name',
68
+ submit: 'list-form-submit',
69
+ cancel: 'list-form-cancel',
70
+ },
71
+ },
72
+ cards: {
73
+ container: 'card-container-{id}',
74
+ title: 'card-title',
75
+ description: 'card-description',
76
+ dueDate: 'card-due-date',
77
+ assignee: 'card-assignee',
78
+ labels: 'card-labels',
79
+ createButton: 'card-create-button',
80
+ editButton: 'card-edit-button',
81
+ deleteButton: 'card-delete-button',
82
+ moveHandle: 'card-move-handle-{id}',
83
+ form: {
84
+ container: 'card-form',
85
+ title: 'card-form-title',
86
+ description: 'card-form-description',
87
+ dueDate: 'card-form-due-date',
88
+ assignee: 'card-form-assignee',
89
+ labels: 'card-form-labels',
90
+ submit: 'card-form-submit',
91
+ cancel: 'card-form-cancel',
92
+ },
93
+ detail: {
94
+ modal: 'card-detail-modal',
95
+ header: 'card-detail-header',
96
+ body: 'card-detail-body',
97
+ comments: 'card-detail-comments',
98
+ attachments: 'card-detail-attachments',
99
+ checklist: 'card-detail-checklist',
100
+ closeButton: 'card-detail-close',
101
+ },
102
+ },
103
+ } as const
104
+
105
+ // =============================================================================
106
+ // KANBAN-SPECIFIC SELECTORS
107
+ // =============================================================================
108
+
109
+ /**
110
+ * Kanban-specific UI selectors.
111
+ */
112
+ export const KANBAN_SELECTORS = {
113
+ board: {
114
+ container: 'kanban-board',
115
+ header: 'kanban-board-header',
116
+ listsContainer: 'kanban-lists-container',
117
+ addListButton: 'kanban-add-list-button',
118
+ settings: 'kanban-board-settings',
119
+ },
120
+ dragDrop: {
121
+ dragging: 'dragging',
122
+ dragHandle: 'drag-handle',
123
+ dropZone: 'drop-zone-{id}',
124
+ dropIndicator: 'drop-indicator',
125
+ placeholder: 'drag-placeholder',
126
+ },
127
+ filters: {
128
+ container: 'kanban-filters',
129
+ search: 'kanban-search',
130
+ assigneeFilter: 'kanban-assignee-filter',
131
+ labelFilter: 'kanban-label-filter',
132
+ dueDateFilter: 'kanban-due-date-filter',
133
+ clearFilters: 'kanban-clear-filters',
134
+ },
135
+ } as const
136
+
137
+ // =============================================================================
138
+ // THEME SELECTORS (CORE + BLOCKS + ENTITIES + KANBAN)
139
+ // =============================================================================
140
+
141
+ /**
142
+ * Complete theme selectors merging core, blocks, and entities.
143
+ */
144
+ export const THEME_SELECTORS = {
145
+ ...CORE_SELECTORS,
146
+ blocks: BLOCK_SELECTORS,
147
+ entities: ENTITY_SELECTORS,
148
+ kanban: KANBAN_SELECTORS,
149
+ } as const
150
+
151
+ // =============================================================================
152
+ // EXPORTS
153
+ // =============================================================================
154
+
155
+ /**
156
+ * Create helpers bound to theme selectors
157
+ */
158
+ const helpers = createSelectorHelpers(THEME_SELECTORS)
159
+
160
+ /**
161
+ * Full selectors object (core + theme extensions)
162
+ */
163
+ export const SELECTORS = helpers.SELECTORS
164
+
165
+ /**
166
+ * Get a selector value by path
167
+ *
168
+ * @example
169
+ * sel('auth.login.form') // 'login-form'
170
+ * sel('entities.boards.list') // 'boards-list'
171
+ * sel('entities.cards.container', { id: 'abc123' }) // 'card-container-abc123'
172
+ */
173
+ export const sel = helpers.sel
174
+
175
+ /**
176
+ * Alias for sel
177
+ */
178
+ export const s = helpers.s
179
+
180
+ /**
181
+ * Get selector only in dev/test environments
182
+ */
183
+ export const selDev = helpers.selDev
184
+
185
+ /**
186
+ * Get Cypress selector string [data-cy="..."]
187
+ *
188
+ * @example
189
+ * cySelector('entities.boards.list') // '[data-cy="boards-list"]'
190
+ */
191
+ export const cySelector = helpers.cySelector
192
+
193
+ /**
194
+ * Create entity-specific selector helpers
195
+ */
196
+ export const entitySelectors = helpers.entitySelectors
197
+
198
+ /**
199
+ * Type exports
200
+ */
201
+ export type ThemeSelectorsType = typeof THEME_SELECTORS
202
+ export type BlockSelectorsType = typeof BLOCK_SELECTORS
203
+ export type EntitySelectorsType = typeof ENTITY_SELECTORS
204
+ export type KanbanSelectorsType = typeof KANBAN_SELECTORS
205
+ export type { Replacements } from '@nextsparkjs/core/lib/test/selector-factory'
206
+ export { CORE_SELECTORS }
@@ -0,0 +1,79 @@
1
+ {
2
+ "productivity": {
3
+ "name": "Productivity",
4
+ "description": "A collaborative task management app",
5
+ "tagline": "Organize your work, together",
6
+
7
+ "navigation": {
8
+ "boards": "Boards",
9
+ "myCards": "My Cards",
10
+ "calendar": "Calendar",
11
+ "team": "Team"
12
+ },
13
+
14
+ "quickActions": {
15
+ "createBoard": "Create Board",
16
+ "createCard": "Add Card"
17
+ },
18
+
19
+ "dashboard": {
20
+ "welcome": "Welcome back!",
21
+ "welcomeWithName": "Welcome back, {name}!",
22
+ "overview": "Your Overview",
23
+ "recentBoards": "Recent Boards",
24
+ "upcomingDeadlines": "Upcoming Deadlines",
25
+ "assignedToYou": "Assigned to You",
26
+ "teamActivity": "Team Activity"
27
+ },
28
+
29
+ "stats": {
30
+ "totalBoards": "Total Boards",
31
+ "totalCards": "Total Cards",
32
+ "cardsCompleted": "Cards Completed",
33
+ "cardsDueThisWeek": "Due This Week"
34
+ },
35
+
36
+ "empty": {
37
+ "noBoards": {
38
+ "title": "No boards yet",
39
+ "description": "Create your first board to start organizing your work.",
40
+ "action": "Create your first board"
41
+ },
42
+ "noAssignedCards": {
43
+ "title": "No cards assigned",
44
+ "description": "Cards assigned to you will appear here."
45
+ },
46
+ "noDeadlines": {
47
+ "title": "No upcoming deadlines",
48
+ "description": "Cards with due dates will appear here."
49
+ }
50
+ },
51
+
52
+ "collaboration": {
53
+ "invite": "Invite Team Member",
54
+ "inviteDescription": "Invite someone to collaborate on your boards",
55
+ "teamMembers": "Team Members",
56
+ "roles": {
57
+ "owner": "Owner",
58
+ "member": "Member",
59
+ "viewer": "Viewer"
60
+ },
61
+ "roleDescriptions": {
62
+ "owner": "Full access to all boards, lists, and cards. Can invite members.",
63
+ "member": "Can create and edit lists and cards. Cannot delete boards.",
64
+ "viewer": "Read-only access to all boards, lists, and cards."
65
+ }
66
+ },
67
+
68
+ "shortcuts": {
69
+ "title": "Keyboard Shortcuts",
70
+ "newCard": "New Card",
71
+ "newBoard": "New Board",
72
+ "search": "Search",
73
+ "toggleSidebar": "Toggle Sidebar",
74
+ "nextList": "Next List",
75
+ "previousList": "Previous List"
76
+ }
77
+ }
78
+ }
79
+
@@ -0,0 +1,79 @@
1
+ {
2
+ "productivity": {
3
+ "name": "Productivity",
4
+ "description": "Una app colaborativa de gestión de tareas",
5
+ "tagline": "Organiza tu trabajo, en equipo",
6
+
7
+ "navigation": {
8
+ "boards": "Tableros",
9
+ "myCards": "Mis Tarjetas",
10
+ "calendar": "Calendario",
11
+ "team": "Equipo"
12
+ },
13
+
14
+ "quickActions": {
15
+ "createBoard": "Crear Tablero",
16
+ "createCard": "Agregar Tarjeta"
17
+ },
18
+
19
+ "dashboard": {
20
+ "welcome": "¡Bienvenido de nuevo!",
21
+ "welcomeWithName": "¡Bienvenido de nuevo, {name}!",
22
+ "overview": "Tu Resumen",
23
+ "recentBoards": "Tableros Recientes",
24
+ "upcomingDeadlines": "Próximas Fechas Límite",
25
+ "assignedToYou": "Asignadas a Ti",
26
+ "teamActivity": "Actividad del Equipo"
27
+ },
28
+
29
+ "stats": {
30
+ "totalBoards": "Total de Tableros",
31
+ "totalCards": "Total de Tarjetas",
32
+ "cardsCompleted": "Tarjetas Completadas",
33
+ "cardsDueThisWeek": "Vencen Esta Semana"
34
+ },
35
+
36
+ "empty": {
37
+ "noBoards": {
38
+ "title": "Aún no hay tableros",
39
+ "description": "Crea tu primer tablero para comenzar a organizar tu trabajo.",
40
+ "action": "Crear tu primer tablero"
41
+ },
42
+ "noAssignedCards": {
43
+ "title": "Sin tarjetas asignadas",
44
+ "description": "Las tarjetas asignadas a ti aparecerán aquí."
45
+ },
46
+ "noDeadlines": {
47
+ "title": "Sin fechas límite próximas",
48
+ "description": "Las tarjetas con fechas de vencimiento aparecerán aquí."
49
+ }
50
+ },
51
+
52
+ "collaboration": {
53
+ "invite": "Invitar Miembro del Equipo",
54
+ "inviteDescription": "Invita a alguien a colaborar en tus tableros",
55
+ "teamMembers": "Miembros del Equipo",
56
+ "roles": {
57
+ "owner": "Propietario",
58
+ "member": "Miembro",
59
+ "viewer": "Visor"
60
+ },
61
+ "roleDescriptions": {
62
+ "owner": "Acceso completo a todos los tableros, listas y tarjetas. Puede invitar miembros.",
63
+ "member": "Puede crear y editar listas y tarjetas. No puede eliminar tableros.",
64
+ "viewer": "Acceso de solo lectura a todos los tableros, listas y tarjetas."
65
+ }
66
+ },
67
+
68
+ "shortcuts": {
69
+ "title": "Atajos de Teclado",
70
+ "newCard": "Nueva Tarjeta",
71
+ "newBoard": "Nuevo Tablero",
72
+ "search": "Buscar",
73
+ "toggleSidebar": "Alternar Barra Lateral",
74
+ "nextList": "Lista Siguiente",
75
+ "previousList": "Lista Anterior"
76
+ }
77
+ }
78
+ }
79
+