jira-pat 1.0.0

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 (95) hide show
  1. package/AGENTS.md +218 -0
  2. package/README.md +64 -0
  3. package/backend/.env.example +1 -0
  4. package/backend/__tests__/getJiraClient.test.js +57 -0
  5. package/backend/__tests__/issues.test.js +565 -0
  6. package/backend/__tests__/jiraService.test.js +1127 -0
  7. package/backend/__tests__/projects.test.js +256 -0
  8. package/backend/coverage/clover.xml +426 -0
  9. package/backend/coverage/coverage-final.json +4 -0
  10. package/backend/coverage/lcov-report/base.css +224 -0
  11. package/backend/coverage/lcov-report/block-navigation.js +87 -0
  12. package/backend/coverage/lcov-report/favicon.png +0 -0
  13. package/backend/coverage/lcov-report/index.html +131 -0
  14. package/backend/coverage/lcov-report/prettify.css +1 -0
  15. package/backend/coverage/lcov-report/prettify.js +2 -0
  16. package/backend/coverage/lcov-report/routes/index.html +131 -0
  17. package/backend/coverage/lcov-report/routes/issues.js.html +823 -0
  18. package/backend/coverage/lcov-report/routes/projects.js.html +190 -0
  19. package/backend/coverage/lcov-report/service/index.html +116 -0
  20. package/backend/coverage/lcov-report/service/jiraService.js.html +1663 -0
  21. package/backend/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  22. package/backend/coverage/lcov-report/sorter.js +210 -0
  23. package/backend/coverage/lcov.info +707 -0
  24. package/backend/index.js +38 -0
  25. package/backend/jest.config.js +11 -0
  26. package/backend/package-lock.json +5636 -0
  27. package/backend/package.json +28 -0
  28. package/backend/routes/issues.js +246 -0
  29. package/backend/routes/projects.js +35 -0
  30. package/backend/service/jiraService.js +526 -0
  31. package/bin/jira.js +92 -0
  32. package/frontend/.env.example +1 -0
  33. package/frontend/coverage/base.css +224 -0
  34. package/frontend/coverage/block-navigation.js +87 -0
  35. package/frontend/coverage/clover.xml +559 -0
  36. package/frontend/coverage/components/CreateIssueModal.jsx.html +592 -0
  37. package/frontend/coverage/components/IssueDetailPanel.jsx.html +1633 -0
  38. package/frontend/coverage/components/IssueDrawer.jsx.html +550 -0
  39. package/frontend/coverage/components/IssueTable.jsx.html +571 -0
  40. package/frontend/coverage/components/SkeletonComponents.jsx.html +223 -0
  41. package/frontend/coverage/components/ToastContainer.jsx.html +142 -0
  42. package/frontend/coverage/components/index.html +191 -0
  43. package/frontend/coverage/coverage-final.json +14 -0
  44. package/frontend/coverage/favicon.png +0 -0
  45. package/frontend/coverage/hooks/index.html +161 -0
  46. package/frontend/coverage/hooks/useFocusTrap.js.html +262 -0
  47. package/frontend/coverage/hooks/useIssueDrawer.js.html +1000 -0
  48. package/frontend/coverage/hooks/useIssuesList.js.html +175 -0
  49. package/frontend/coverage/hooks/useToasts.js.html +142 -0
  50. package/frontend/coverage/index.html +161 -0
  51. package/frontend/coverage/prettify.css +1 -0
  52. package/frontend/coverage/prettify.js +2 -0
  53. package/frontend/coverage/services/api.js.html +547 -0
  54. package/frontend/coverage/services/index.html +116 -0
  55. package/frontend/coverage/sort-arrow-sprite.png +0 -0
  56. package/frontend/coverage/sorter.js +210 -0
  57. package/frontend/coverage/utils/index.html +131 -0
  58. package/frontend/coverage/utils/issueHelpers.jsx.html +334 -0
  59. package/frontend/coverage/utils/sanitize.js.html +166 -0
  60. package/frontend/index.html +13 -0
  61. package/frontend/package-lock.json +3436 -0
  62. package/frontend/package.json +30 -0
  63. package/frontend/src/App.jsx +447 -0
  64. package/frontend/src/__tests__/components/CreateIssueModal.test.jsx +375 -0
  65. package/frontend/src/__tests__/components/IssueDetailPanel.test.jsx +962 -0
  66. package/frontend/src/__tests__/components/IssueDrawer.test.jsx +240 -0
  67. package/frontend/src/__tests__/components/IssueTable.test.jsx +423 -0
  68. package/frontend/src/__tests__/components/ToastContainer.test.jsx +196 -0
  69. package/frontend/src/__tests__/hooks/useFocusTrap.test.js +197 -0
  70. package/frontend/src/__tests__/hooks/useIssueDrawer.test.js +1053 -0
  71. package/frontend/src/__tests__/hooks/useIssuesList.test.js +175 -0
  72. package/frontend/src/__tests__/hooks/useToasts.test.js +110 -0
  73. package/frontend/src/__tests__/services/api.test.js +568 -0
  74. package/frontend/src/__tests__/setup.js +54 -0
  75. package/frontend/src/__tests__/utils/issueHelpers.test.jsx +336 -0
  76. package/frontend/src/__tests__/utils/sanitize.test.js +238 -0
  77. package/frontend/src/components/CreateIssueModal.jsx +169 -0
  78. package/frontend/src/components/ErrorBoundary.jsx +52 -0
  79. package/frontend/src/components/IssueDetailPanel.jsx +517 -0
  80. package/frontend/src/components/IssueDrawer.jsx +155 -0
  81. package/frontend/src/components/IssueTable.jsx +162 -0
  82. package/frontend/src/components/SkeletonComponents.jsx +46 -0
  83. package/frontend/src/components/StandaloneIssuePage.jsx +176 -0
  84. package/frontend/src/components/ToastContainer.jsx +19 -0
  85. package/frontend/src/hooks/useFocusTrap.js +59 -0
  86. package/frontend/src/hooks/useIssueDrawer.js +305 -0
  87. package/frontend/src/hooks/useIssuesList.js +30 -0
  88. package/frontend/src/hooks/useToasts.js +19 -0
  89. package/frontend/src/index.css +2070 -0
  90. package/frontend/src/main.jsx +13 -0
  91. package/frontend/src/services/api.js +154 -0
  92. package/frontend/src/utils/issueHelpers.jsx +84 -0
  93. package/frontend/src/utils/sanitize.js +27 -0
  94. package/frontend/vite.config.js +15 -0
  95. package/package.json +19 -0
package/AGENTS.md ADDED
@@ -0,0 +1,218 @@
1
+ # AGENTS.md - Jira Dashboard
2
+
3
+ ## Project Structure
4
+
5
+ ```
6
+ jira-dashboard/
7
+ ├── package.json # Root - runs both servers with concurrently
8
+ ├── backend/ # Express API (CommonJS), port 5000
9
+ │ ├── index.js # Entry point
10
+ │ ├── routes/ # API route handlers
11
+ │ └── service/ # Jira API integration (axios + node-cache)
12
+ └── frontend/ # React app (ES modules + Vite), port 5173
13
+ ├── src/
14
+ │ ├── App.jsx # Main component (~400 lines)
15
+ │ ├── index.css # All styles (~1700 lines)
16
+ │ ├── services/ # API client (axios)
17
+ │ ├── components/ # React components
18
+ │ │ ├── IssueDrawer.jsx
19
+ │ │ ├── CreateIssueModal.jsx
20
+ │ │ ├── IssueTable.jsx
21
+ │ │ ├── StandaloneIssuePage.jsx
22
+ │ │ └── ToastContainer.jsx
23
+ │ └── hooks/ # Custom React hooks
24
+ │ ├── useIssueDrawer.js
25
+ │ ├── useToasts.js
26
+ │ └── useIssuesList.js
27
+ └── vite.config.js
28
+ ```
29
+
30
+ ## Commands
31
+
32
+ ```bash
33
+ # Root - runs both servers
34
+ npm start
35
+
36
+ # Frontend
37
+ cd frontend && npm run dev # Dev server (port 5173)
38
+ cd frontend && npm run build # Production build
39
+ cd frontend && npm run preview # Preview build
40
+
41
+ # Backend
42
+ cd backend && npm start # Start server (port 5000)
43
+ ```
44
+
45
+ ## Environment Setup
46
+
47
+ Create `/backend/.env`:
48
+ ```
49
+ JIRA_BASE_URL=https://your-domain.atlassian.net
50
+ JIRA_EMAIL=your-email@example.com
51
+ JIRA_PAT=your-api-token
52
+ PORT=5000
53
+ BACKEND_BASE_URL=http://localhost:5000
54
+ ```
55
+
56
+ Create `/frontend/.env` (optional):
57
+ ```
58
+ VITE_API_BASE_URL=http://localhost:5000/api
59
+ ```
60
+
61
+ ## Code Style
62
+
63
+ ### General
64
+ - No comments unless explaining non-obvious logic
65
+ - Small, focused functions (single responsibility)
66
+ - Prefer early returns to reduce nesting
67
+ - 4-space indentation (backend), standard formatting (frontend)
68
+
69
+ ### Frontend (React + Vite - ES Modules)
70
+ - `.jsx` for React components, `.js` for utilities
71
+ - Named exports for utilities, default export for components
72
+ - Functional components with hooks only (no class components)
73
+ - Import order: React → external libs → internal modules → CSS
74
+
75
+ ```jsx
76
+ import React, { useState, useEffect } from 'react';
77
+ import { Search, X } from 'lucide-react';
78
+ import { getIssues } from './services/api';
79
+ import './index.css';
80
+ ```
81
+
82
+ ### Backend (Node.js - CommonJS)
83
+ - Use `require()` and `module.exports`
84
+ - Keep routes thin, services thick
85
+
86
+ ```javascript
87
+ const express = require('express');
88
+ const router = express.Router();
89
+ module.exports = router;
90
+ ```
91
+
92
+ ### Naming Conventions
93
+
94
+ | Type | Convention | Example |
95
+ |------|------------|---------|
96
+ | Components | PascalCase | `IssueDrawer` |
97
+ | Functions | camelCase | `fetchIssues` |
98
+ | Constants | UPPER_SNAKE | `STATUS_CHIPS` |
99
+ | CSS Classes | kebab-case | `.issue-table` |
100
+ | Files | kebab-case | `api-client.js` |
101
+ | React state | `useXxx`, `setXxx` | `issues`, `setIssues` |
102
+
103
+ ## Common Patterns
104
+
105
+ ### Debounce (search inputs)
106
+ ```jsx
107
+ useEffect(() => {
108
+ const timer = setTimeout(() => fetchData(), 500);
109
+ return () => clearTimeout(timer);
110
+ }, [searchQuery]);
111
+ ```
112
+
113
+ ### Async with Loading/Error
114
+ ```jsx
115
+ const [loading, setLoading] = useState(false);
116
+ const [error, setError] = useState(null);
117
+
118
+ const fetchData = async () => {
119
+ setLoading(true);
120
+ setError(null);
121
+ try {
122
+ const data = await apiCall();
123
+ setData(data);
124
+ } catch (err) {
125
+ setError(err.message || 'Something went wrong');
126
+ } finally {
127
+ setLoading(false);
128
+ }
129
+ };
130
+ ```
131
+
132
+ ### Toast Notifications
133
+ ```jsx
134
+ const [toasts, setToasts] = useState([]);
135
+ const addToast = (message, type = 'info') => {
136
+ const id = Date.now();
137
+ setToasts(prev => [...prev, { id, message, type }]);
138
+ setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 4000);
139
+ };
140
+ ```
141
+
142
+ ### Keyboard Shortcuts
143
+ ```jsx
144
+ useEffect(() => {
145
+ const handleKeyDown = (e) => {
146
+ if (e.key === '/' && !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {
147
+ e.preventDefault();
148
+ }
149
+ };
150
+ window.addEventListener('keydown', handleKeyDown);
151
+ return () => window.removeEventListener('keydown', handleKeyDown);
152
+ }, []);
153
+ ```
154
+
155
+ ## Error Handling
156
+
157
+ ### Backend API Routes
158
+ ```javascript
159
+ router.get('/:id', async (req, res) => {
160
+ try {
161
+ const data = await service.getData(req.params.id);
162
+ if (!data) return res.status(404).json({ error: 'Not found' });
163
+ res.json(data);
164
+ } catch (error) {
165
+ console.error('Error:', error.message);
166
+ res.status(500).json({ error: 'Server error', details: error.message });
167
+ }
168
+ });
169
+ ```
170
+
171
+ ### Frontend
172
+ ```jsx
173
+ {error && (
174
+ <div className="error" role="alert">
175
+ <AlertCircle size={16} />
176
+ {error}
177
+ </div>
178
+ )}
179
+ ```
180
+
181
+ ## Accessibility
182
+ - Always use `aria-label` on icon-only buttons
183
+ - Use semantic HTML (`<button>`, `<nav>`, `<main>`)
184
+ - Support keyboard navigation (Tab, Enter, Escape, Arrow keys)
185
+
186
+ ```jsx
187
+ <button onClick={handleDelete} aria-label="Delete item">
188
+ <TrashIcon size={16} />
189
+ </button>
190
+ ```
191
+
192
+ ## CSS Guidelines
193
+ - Use CSS custom properties (variables) for colors, spacing
194
+ - BEM-inspired naming for component styles
195
+ - Avoid inline styles except for dynamic values
196
+
197
+ ```css
198
+ :root {
199
+ --primary: #0052cc;
200
+ --spacing-sm: 8px;
201
+ --spacing-md: 16px;
202
+ }
203
+
204
+ .button { padding: var(--spacing-sm) var(--spacing-md); }
205
+ .button--loading { opacity: 0.7; }
206
+ ```
207
+
208
+ ## Jira API Integration
209
+ - Uses axios with 10-second timeout
210
+ - 60-second in-memory cache (node-cache)
211
+ - Basic Auth with Jira PAT for authentication
212
+ - Uses JQL for searching issues
213
+
214
+ ## Security
215
+ - Never commit `.env` files (in `.gitignore`)
216
+ - API credentials use Basic Auth with Jira PAT
217
+ - File uploads limited to 10MB
218
+ - Input validation on all API endpoints
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # Jira Dashboard
2
+
3
+ A fast, lightweight, and modern Jira Dashboard built with React, Vite, and Node.js. It avoids CORS and proxy issues by connecting directly to the Jira Cloud API using Basic Authentication within a dedicated Express backend.
4
+
5
+ ## Features Currently Supported
6
+
7
+ 1. **Direct Jira Cloud Integration**: Uses your Atlassian Email and API Token securely to fetch real-time Jira data.
8
+ 2. **"My Assigned Issues" View**: By default, the API automatically fetches tickets currently assigned to you (`currentUser()`).
9
+ 3. **Dynamic Filtering**:
10
+ - Filter by **Project Key** (e.g. `ABC`)
11
+ - Search by **Summary / Text** content
12
+ - Filter by **Ticket Status** (To Do, In Progress, Done, Closed)
13
+ 4. **Interactive Side Drawer**: Click an issue row to load real-time deep-dive details.
14
+ - **Rich Media & Comments**: Renders descriptions and comments with inline images seamlessly proxied securely from Jira.
15
+ - **Subtasks & Linked Issues**: Visual hierarchy mapping of attached Jira issues, enabling clickable exploration.
16
+ - **Issue Transitions**: Update ticket stages (e.g., "To Do" -> "In Progress") directly within the app.
17
+ - **Ticket Assignment**: Searchable user dropdown directly mapped to your active Jira user directory.
18
+ - **File Attachments**: One-click upload functionality for files mapped directly onto tickets.
19
+ 5. **Create New Issues**: Global "+" action triggering dynamic Issue schemas directly synced from Jira projects.
20
+ 6. **Fast, Modern UI**: Refactored with `lucide-react` icons, toast notifications, and animated skeleton loaders.
21
+ 7. **Keyboard Navigation**: Press `/` to focus search, `Enter` to open an issue, and `Esc` to close modals.
22
+ 8. **Backend Caching & Proxying**: 60-second in-memory caching via `node-cache` bypassing CORS/Auth blocks.
23
+ 9. **Single Command Start**: Run both the React frontend and Node backend simultaneously using `npm start`.
24
+
25
+ ---
26
+
27
+ ## Project Structure
28
+
29
+ ```
30
+ jira-dashboard/
31
+ ├── package.json # Root configuration for concurrently running both servers
32
+ ├── README.md # This file
33
+
34
+ ├── backend/ # Node.js + Express API Layer
35
+ │ ├── .env # Secrets (JIRA_BASE_URL, JIRA_EMAIL, JIRA_PAT)
36
+ │ ├── index.js # Main Express server and configuration
37
+ │ ├── package.json # Backend dependencies (express, axios, cors)
38
+ │ ├── routes/
39
+ │ │ └── issues.js # Route logic for /api/issues
40
+ │ └── service/
41
+ │ └── jiraService.js# Jira REST API communication & JQL construction
42
+
43
+ └── frontend/ # React + Vite UI
44
+ ├── package.json # Frontend dependencies (react, lucide-react)
45
+ ├── vite.config.js # Vite configuration
46
+ ├── index.html # App entry HTML
47
+ └── src/
48
+ ├── main.jsx # React DOM entry point
49
+ ├── App.jsx # Main Dashboard View and Filters
50
+ ├── index.css # Clean, modern UI Styling
51
+ └── services/
52
+ └── api.js # Axios client connecting to backend /api/issues
53
+ ```
54
+
55
+ ---
56
+
57
+ ## How To Run
58
+
59
+ 1. Open `/backend/.env` and ensure your `JIRA_BASE_URL`, `JIRA_EMAIL`, and `JIRA_PAT` (Atlassian API Token) are filled in.
60
+ 2. Run the application from the root directory:
61
+ ```bash
62
+ npm start
63
+ ```
64
+ 3. Open your browser to `http://localhost:5173`.
@@ -0,0 +1 @@
1
+ BACKEND_BASE_URL=http://localhost:5000
@@ -0,0 +1,57 @@
1
+ jest.mock('dotenv', () => ({ config: jest.fn() }));
2
+ jest.mock('node-cache');
3
+
4
+ const NodeCache = require('node-cache');
5
+ const mockCacheInstance = {
6
+ get: jest.fn(),
7
+ set: jest.fn(),
8
+ del: jest.fn(),
9
+ flushAll: jest.fn(),
10
+ keys: jest.fn(() => [])
11
+ };
12
+ NodeCache.mockImplementation(() => mockCacheInstance);
13
+
14
+ describe('getJiraClient config validation', () => {
15
+ beforeEach(() => {
16
+ jest.resetModules();
17
+ jest.clearAllMocks();
18
+ });
19
+
20
+ it('should throw when JIRA_BASE_URL and JIRA_PAT are not set', async () => {
21
+ delete process.env.JIRA_BASE_URL;
22
+ delete process.env.JIRA_PAT;
23
+ delete process.env.JIRA_EMAIL;
24
+ jest.mock('axios');
25
+ const service = require('../service/jiraService');
26
+ await expect(service.searchIssues({})).rejects.toThrow(
27
+ 'JIRA_BASE_URL or JIRA_PAT is not configured in .env'
28
+ );
29
+ process.env.JIRA_BASE_URL = 'https://test.atlassian.net';
30
+ process.env.JIRA_PAT = 'test-pat';
31
+ });
32
+
33
+ it('should use Bearer auth when JIRA_EMAIL is not set', async () => {
34
+ delete process.env.JIRA_EMAIL;
35
+ process.env.JIRA_BASE_URL = 'https://test.atlassian.net';
36
+ process.env.JIRA_PAT = 'test-pat';
37
+ process.env.BACKEND_BASE_URL = 'http://localhost:5000';
38
+ jest.mock('axios');
39
+ const axios = require('axios');
40
+ const mockAxiosInstance = {
41
+ get: jest.fn().mockResolvedValue({ data: { issues: [], total: 0 } }),
42
+ post: jest.fn(),
43
+ put: jest.fn(),
44
+ defaults: { baseURL: 'https://test.atlassian.net' }
45
+ };
46
+ axios.create.mockReturnValue(mockAxiosInstance);
47
+ const service = require('../service/jiraService');
48
+ await service.searchIssues({ project: 'TEST' });
49
+ expect(axios.create).toHaveBeenCalledWith(
50
+ expect.objectContaining({
51
+ headers: expect.objectContaining({
52
+ Authorization: expect.stringMatching(/^Bearer /)
53
+ })
54
+ })
55
+ );
56
+ });
57
+ });