diff --git a/README.md b/README.md index 94d5c98..4f8778c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ The .env_sample file shows the necessary configuration keywords, including a personal access token for GitHub API access and the target GitHub repository name. +To create a GitHub API token, open your personal GitHub Settings, and select "Developer Settings" from the sidebar. Open "Personal access tokens" and select "Tokens (classic)" + +Click on "Generate new token" and select the "Generate new token (classic)" option from the pulldown. You can set a Note to identify this particular token. Select the Expiration (30 days default), and "repo". Make sure you copy and save the generated token right away, because you can't display it again. + #### Frontend ```bash diff --git a/frontend/src/components/CommentCard.test.jsx b/frontend/src/components/CommentCard.test.jsx index b0d25ae..3004667 100644 --- a/frontend/src/components/CommentCard.test.jsx +++ b/frontend/src/components/CommentCard.test.jsx @@ -71,7 +71,9 @@ describe('CommentCard', () => { render(); await user.click(screen.getByRole('button', { name: 'Edit comment' })); - expect(screen.getByText('Edit comment')).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'Edit comment' }) + ).toBeInTheDocument(); expect(screen.getByPlaceholderText('Write markdown…')).toHaveValue( mockComment.body ); diff --git a/frontend/src/components/ProjectStatusPanel.jsx b/frontend/src/components/ProjectStatusPanel.jsx index 314b28a..62a975c 100644 --- a/frontend/src/components/ProjectStatusPanel.jsx +++ b/frontend/src/components/ProjectStatusPanel.jsx @@ -1,4 +1,5 @@ // Generated-by: Cursor +// Assisted-by: openai-code-assist import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Alert, @@ -165,8 +166,8 @@ const StatusSection = ({ const ProjectStatusPanel = () => { const initial = useMemo(() => getInitialDateRange(), []); - const [startDate, setStartDate] = useState(initial.start); - const [endDate, setEndDate] = useState(initial.end); + const [startDate, setStartDate] = useState(null); + const [endDate, setEndDate] = useState(null); const [draftStartDate, setDraftStartDate] = useState(initial.start); const [draftEndDate, setDraftEndDate] = useState(initial.end); const [loading, setLoading] = useState(false); @@ -188,10 +189,16 @@ const ProjectStatusPanel = () => { }, []); useEffect(() => { + if (!startDate || !endDate) { + return; + } load(startDate, endDate); }, [startDate, endDate, load]); useEffect(() => { + if (!startDate || !endDate) { + return; + } try { localStorage.setItem(STORAGE_START_KEY, startDate); localStorage.setItem(STORAGE_END_KEY, endDate); @@ -202,8 +209,8 @@ const ProjectStatusPanel = () => { const onApply = () => { if (draftStartDate > draftEndDate) { - setDraftStartDate(startDate); - setDraftEndDate(endDate); + setDraftStartDate(startDate ?? initial.start); + setDraftEndDate(endDate ?? initial.end); return; } setStartDate(draftStartDate); diff --git a/frontend/src/components/ProjectStatusPanel.test.jsx b/frontend/src/components/ProjectStatusPanel.test.jsx index 932d025..c7add90 100644 --- a/frontend/src/components/ProjectStatusPanel.test.jsx +++ b/frontend/src/components/ProjectStatusPanel.test.jsx @@ -1,4 +1,5 @@ // Generated-by: Cursor +// Assisted-by: openai-code-assist import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -59,8 +60,12 @@ describe('ProjectStatusPanel', () => { }); it('loads report and renders linked rows', async () => { + const user = userEvent.setup(); render(); + expect(api.fetchProjectStatusReport).not.toHaveBeenCalled(); + await user.click(screen.getByRole('button', { name: 'Apply' })); + await waitFor(() => { expect(api.fetchProjectStatusReport).toHaveBeenCalledWith( '2025-04-03', @@ -81,6 +86,7 @@ describe('ProjectStatusPanel', () => { }); it('shows days since update in PR backlog section', async () => { + const user = userEvent.setup(); api.fetchProjectStatusReport.mockResolvedValue({ start_date: '2025-04-04', end_date: '2025-04-10', @@ -100,6 +106,8 @@ describe('ProjectStatusPanel', () => { ], }); render(); + expect(api.fetchProjectStatusReport).not.toHaveBeenCalled(); + await user.click(screen.getByRole('button', { name: 'Apply' })); await waitFor(() => { expect(screen.getByText('Stale open')).toBeInTheDocument(); }); @@ -110,6 +118,9 @@ describe('ProjectStatusPanel', () => { const user = userEvent.setup(); render(); + expect(api.fetchProjectStatusReport).not.toHaveBeenCalled(); + await user.click(screen.getByRole('button', { name: 'Apply' })); + await waitFor(() => { expect( screen.getByRole('button', { @@ -160,10 +171,7 @@ describe('ProjectStatusPanel', () => { }); render(); - - await waitFor(() => { - expect(api.fetchProjectStatusReport).toHaveBeenCalled(); - }); + expect(api.fetchProjectStatusReport).not.toHaveBeenCalled(); await user.clear(screen.getByLabelText('Starting')); await user.type(screen.getByLabelText('Starting'), '2025-04-01'); @@ -200,15 +208,13 @@ describe('ProjectStatusPanel', () => { render(); - await waitFor(() => { - expect(api.fetchProjectStatusReport).toHaveBeenCalledTimes(1); - }); + expect(api.fetchProjectStatusReport).not.toHaveBeenCalled(); await user.click(screen.getByRole('button', { name: 'Today' })); expect(screen.getByLabelText('Starting')).toHaveValue(start); expect(screen.getByLabelText('Ending')).toHaveValue(today); - expect(api.fetchProjectStatusReport).toHaveBeenCalledTimes(1); + expect(api.fetchProjectStatusReport).not.toHaveBeenCalled(); expect(localStorage.getItem(STORAGE_START_KEY)).toBe('2025-04-03'); expect(localStorage.getItem(STORAGE_END_KEY)).toBe('2025-04-10');