Conversation
There was a problem hiding this comment.
Pull request overview
Implements the Cycles page end-to-end by expanding the UI (list + active-cycle insights, create/edit/delete flows, filters, favorites) and aligning backend cycle status behavior with date-derived “Plane-style” statuses.
Changes:
- Added Cycle CRUD support in the UI (service + Create/Edit modals) and implemented the Cycles page UI with filtering + insights.
- Introduced local “favorite cycles” (localStorage + cross-component eventing) and surfaced favorites in the sidebar.
- Updated backend cycle status to be computed from start/end dates on List/Get/Create/Update.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/services/cycleService.ts | Adds Cycle create/update/delete + listIssueIds API calls for the new Cycles UI. |
| ui/src/pages/ModulesPage.tsx | Minor UI tweak to module name styling (removes hover underline). |
| ui/src/pages/CyclesPage.tsx | Replaces placeholder with full Cycles page: active-cycle section, upcoming/completed lists, favorites, edit/delete actions, filtering event handling. |
| ui/src/lib/projectCyclesEvents.ts | Adds window event constants to sync cycles filtering/refresh across header + page. |
| ui/src/hooks/useCycleFavorites.ts | Implements localStorage-backed cycle favorites with a global change event. |
| ui/src/components/layout/Sidebar.tsx | Loads and displays favorite cycles in the favorites section; listens for cycle favorites changes. |
| ui/src/components/layout/PageHeader.tsx | Adds Cycles-specific Filters dropdown and “Add cycle” button; dispatches filter/refresh events; mounts CreateCycleModal. |
| ui/src/components/layout/AppShell.tsx | Adjusts main content padding for the Cycles page layout. |
| ui/src/components/UpdateCycleModal.tsx | Adds modal for editing a cycle (title/description/date range) via cycleService.update. |
| ui/src/components/CreateCycleModal.tsx | Adds modal for creating cycles (project selection + fields) via cycleService.create. |
| ui/package.json | Bumps UI version to 0.5.0. |
| ui/package-lock.json | Updates lockfile version metadata to 0.5.0. |
| api/internal/service/cycle.go | Computes cycle status from start/end dates during reads and after create/update. |
| api/internal/model/cycle.go | Changes cycle JSON tags for start/end dates (removes omitempty). |
| api/internal/handler/cycle.go | Enhances cycle date parsing to accept RFC3339 timestamps and date-only strings. |
Files not reviewed (1)
- ui/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)
api/internal/handler/cycle.go:31
parseOptionalTimereturnsnilwhen parsing fails, which means invalidstart_date/end_datevalues are silently treated as “not provided” (e.g. Create would succeed and produce a draft cycle). Consider returning an error from the parser and responding with 400 (similar to IssueHandler’s date validation) so clients get clear feedback on malformed dates.
func parseOptionalTime(s string) *time.Time {
if s == "" {
return nil
}
// Try RFC3339 first (full timestamp), then date-only YYYY-MM-DD
t, err := time.Parse(time.RFC3339, s)
if err == nil {
return &t
}
t, err = time.Parse("2006-01-02", s)
if err != nil {
return nil
}
return &t
api/internal/service/cycle.go:149
- Cycle
Updatestill accepts astatusargument, but the service no longer applies it (status is recomputed from dates). This creates an API footgun where clients can sendstatusand get it ignored. Either remove/ignorestatusat the handler level (don’t bind/forward it) or make the service signature not accept it and/or return 400 if a status is provided.
func (s *CycleService) Update(ctx context.Context, workspaceSlug string, projectID, cycleID uuid.UUID, userID uuid.UUID, name, description, status string, startDate, endDate *time.Time) (*model.Cycle, error) {
cy, err := s.Get(ctx, workspaceSlug, projectID, cycleID, userID)
if err != nil {
return nil, err
}
if name != "" {
cy.Name = name
}
if description != "" {
cy.Description = description
}
if startDate != nil {
cy.StartDate = startDate
}
if endDate != nil {
cy.EndDate = endDate
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR implements the Cycles page
Type of Change
Closes #30