Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c353176
feat: add workspace sprint planning
alanssant0s Jun 19, 2026
ad69564
ci: promote development images to main
alanssant0s Jun 19, 2026
eb5049e
Merge pull request #1 from alanssant0s/plane-sprint-analysis
alanssant0s Jun 19, 2026
7d9583a
feat: implement workspace sprint squads and automation members
alanssant0s Jun 19, 2026
3e610e8
Handle routeFilters for sprint views
alanssant0s Jun 19, 2026
29b87c7
Merge pull request #3 from alanssant0s/plane-sprint-analysis
alanssant0s Jun 19, 2026
baaefad
ci: deploy Dokploy after main image promotion
alanssant0s Jun 19, 2026
87dbc2a
Merge pull request #5 from alanssant0s/plane-sprint-analysis
alanssant0s Jun 19, 2026
e559327
refactor: remove forum references and unused components
alanssant0s Jun 21, 2026
d1db722
feat: enhance issue pagination and grouping functionality
alanssant0s Jun 21, 2026
b2938b9
fix: address pagination issues in WorkspaceViewIssuesViewSet
alanssant0s Jun 21, 2026
3057414
Merge pull request #7 from alanssant0s/squads-adjusts
alanssant0s Jun 21, 2026
eaaf13f
feat: add assigned-to-me issue filter
alanssant0s Jun 21, 2026
6381e4e
feat: extend assigned-to-me filtering
alanssant0s Jun 21, 2026
cddec89
Merge pull request #8 from alanssant0s/squads-adjusts
alanssant0s Jun 21, 2026
3a070b0
feat: expose project pages via API v1
alanssant0s Jun 21, 2026
ae94598
Merge pull request #10 from alanssant0s/squads-adjusts
alanssant0s Jun 21, 2026
d1b2111
feat(pages): add slash command to link to another page
alanssant0s Jun 21, 2026
16076c5
fix(pages): persist page list sort preference in localStorage
alanssant0s Jun 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .cursor/rules/pr-deploy-publicacao.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
description: Fluxo padrao para revisar, testar, mergear PRs e publicar em main
alwaysApply: true
---

# PR, Deploy e Publicacao

Quando o usuario pedir para aceitar PRs, fazer deploy, publicar na `main` ou gerar/promover imagem Docker, siga este fluxo.

1. Identifique o escopo antes de agir: liste PRs abertos com `gh pr list`, confirme repositorio, branch base (`development`, `main` ou a branch padrao do repo) e se ha worktree suja. Nao inclua `.env`, dumps, credenciais, venvs ou arquivos nao relacionados.
2. Analise o conteudo do PR inteiro, nao apenas o ultimo commit: use `gh pr view`, `gh pr diff`, `git log base..head` e `git diff base...head`.
3. Se houver dois ou mais PRs para o mesmo destino, teste o estado combinado sobre a branch base atualizada antes de mergear. Crie uma branch local temporaria, aplique os heads e resolva conflitos com cuidado.
4. Rode validacoes locais antes do merge conforme o escopo:
- Monorepo/web/packages: `pnpm install --frozen-lockfile`, testes especificos adicionados/afetados, `pnpm build` ou build filtrado, e `pnpm check:lint` como informativo.
- API: testes relevantes via Docker conforme `apps/api/tests/RUNNING_TESTS.md`; use `docker compose -f docker-compose-test.yml run --rm api-tests pytest ...` para subsets e rode checagens/migrations relevantes quando houver alteracoes de modelo.
5. Considere lint nao bloqueante apenas quando a falha vier de debitos antigos fora dos arquivos alterados. Reporte isso no resumo e confira diagnósticos dos arquivos tocados.
6. Quando o PR base for `development`, mergeie primeiro em `development`, aguarde o workflow de build/push da imagem Docker e so depois crie PR `development -> main`.
7. Para publicar na `main`, abra PR com resumo do que sera publicado e plano de testes. Mergeie apenas se `mergeStateStatus` estiver `CLEAN` e os checks relevantes estiverem verdes.
8. Apos merge na `main`, acompanhe o workflow disparado em `main` ate concluir. Se houver promocao de imagem ja buildada em `development`, confirme que o workflow promoveu a imagem correta e disparou os webhooks esperados.
9. Se CI falhar, leia `gh run view <id> --log-failed`, corrija a causa raiz em novo PR, mergeie e acompanhe novamente. Para falhas Docker/pnpm, verifique `pnpm-workspace.yaml`, `pnpm fetch/install --offline`, versao do pnpm, versao do Node da imagem e compatibilidade com lockfile.
10. Ao finalizar, responda com links dos PRs, runs, checks executados e qualquer risco residual.

Nao use push direto para `main` ou `development` quando um PR for apropriado. Nao force push nem use comandos destrutivos sem pedido explicito.
22 changes: 22 additions & 0 deletions .cursor/rules/pre-commit-validations.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Validacoes obrigatorias antes de commit e PR
alwaysApply: true
---

# Pre-Commit e Validacoes

Quando o usuario pedir para commitar, validar commit, preparar PR ou corrigir erro de hook, siga este fluxo.

1. Use o hook real do repo: `.husky/pre-commit` roda `pnpm lint-staged`. Nao use `pre-commit run`, pois este repo nao usa `.pre-commit-config.yaml`.
2. Antes de rodar o hook, confira `git status --short --branch` e preserve alteracoes existentes do usuario. Nao reverta arquivos sem pedido explicito.
3. O `lint-staged` valida apenas arquivos staged. Se corrigir arquivos que fazem parte do commit, stageie essas correcoes antes de reexecutar `pnpm lint-staged`.
4. Trate warnings do `pnpm exec oxlint --fix --deny-warnings` como bloqueantes. Corrija a causa raiz em vez de ignorar o hook.
5. Para falhas comuns:
- `consistent-function-scoping`: mova funcoes que nao capturam escopo para fora do componente/funcao.
- `promise(always-return)`: retorne o valor dentro de `.then()` ou lance erro.
- `no-shadow`: renomeie variaveis locais/desestruturadas que colidem com nomes do escopo externo.
- `no-unneeded-ternary`: substitua `cond ? true : false` por `cond`.
- `no-map-spread`: use `Object.assign({}, item, updates)` quando precisar manter copy-on-write.
6. Depois que o hook passar, confira `git status --short --branch` e `git diff --cached --stat` para avisar quais arquivos ficaram staged.

Se `oxfmt` ou `oxlint --fix` modificar arquivos, inclua essas modificacoes no stage antes de tentar o commit novamente.
175 changes: 175 additions & 0 deletions .github/workflows/docker-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: Build Production Docker Images

on:
workflow_dispatch:
inputs:
app_release:
description: "Image tag to publish and use as APP_RELEASE"
required: false
type: string
push:
branches:
- development
- main
- master
- preview
- canary

permissions:
contents: read
packages: write

env:
PRODUCTION_APP_BASE_URL: ${{ vars.PRODUCTION_APP_BASE_URL || 'https://plane.agncflamingo.com.br' }}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-push:
name: Build ${{ matrix.image }}
if: github.ref_name != 'main'
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- image: plane-frontend
context: .
dockerfile: ./apps/web/Dockerfile.web
- image: plane-space
context: .
dockerfile: ./apps/space/Dockerfile.space
- image: plane-admin
context: .
dockerfile: ./apps/admin/Dockerfile.admin
- image: plane-live
context: .
dockerfile: ./apps/live/Dockerfile.live
- image: plane-backend
context: ./apps/api
dockerfile: ./apps/api/Dockerfile.api
- image: plane-proxy
context: ./apps/proxy
dockerfile: ./apps/proxy/Dockerfile.ce

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Prepare image variables
id: vars
shell: bash
run: |
image_namespace="${GITHUB_REPOSITORY,,}"
app_release="${{ github.event.inputs.app_release }}"

if [ -z "$app_release" ]; then
app_release="$(echo "$GITHUB_REF_NAME" | tr '[:upper:]' '[:lower:]' | sed 's#[^a-z0-9._-]#-#g')"
fi

echo "image_namespace=$image_namespace" >> "$GITHUB_OUTPUT"
echo "app_release=$app_release" >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ steps.vars.outputs.image_namespace }}/${{ matrix.image }}
tags: |
type=raw,value=${{ steps.vars.outputs.app_release }}
type=sha,prefix=sha-,format=short
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
VITE_API_BASE_URL=${{ env.PRODUCTION_APP_BASE_URL }}
VITE_ADMIN_BASE_URL=${{ env.PRODUCTION_APP_BASE_URL }}
VITE_SPACE_BASE_URL=${{ env.PRODUCTION_APP_BASE_URL }}
VITE_LIVE_BASE_URL=${{ env.PRODUCTION_APP_BASE_URL }}
VITE_WEB_BASE_URL=${{ env.PRODUCTION_APP_BASE_URL }}

promote-main:
name: Promote ${{ matrix.image }} to main
if: github.ref_name == 'main'
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
image:
- plane-frontend
- plane-space
- plane-admin
- plane-live
- plane-backend
- plane-proxy

steps:
- name: Prepare image variables
id: vars
shell: bash
run: |
image_namespace="${GITHUB_REPOSITORY,,}"
image="ghcr.io/${image_namespace}/${{ matrix.image }}"

echo "source_image=${image}:development" >> "$GITHUB_OUTPUT"
echo "main_image=${image}:main" >> "$GITHUB_OUTPUT"
echo "latest_image=${image}:latest" >> "$GITHUB_OUTPUT"
echo "sha_image=${image}:sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Promote development image
shell: bash
run: |
docker buildx imagetools inspect "${{ steps.vars.outputs.source_image }}"
docker buildx imagetools create \
-t "${{ steps.vars.outputs.main_image }}" \
-t "${{ steps.vars.outputs.latest_image }}" \
-t "${{ steps.vars.outputs.sha_image }}" \
"${{ steps.vars.outputs.source_image }}"

deploy-dokploy:
name: Deploy Dokploy compose
if: github.ref_name == 'main'
needs: promote-main
runs-on: ubuntu-22.04
steps:
- name: Trigger Dokploy deployment
env:
DOKPLOY_URL: ${{ secrets.DOKPLOY_URL }}
DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }}
DOKPLOY_COMPOSE_ID: ${{ secrets.DOKPLOY_COMPOSE_ID }}
shell: bash
run: |
curl --fail-with-body --request POST "${DOKPLOY_URL}/api/compose.deploy" \
--header "Content-Type: application/json" \
--header "x-api-key: ${DOKPLOY_API_KEY}" \
--data "{\"composeId\":\"${DOKPLOY_COMPOSE_ID}\"}"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ pnpm-debug.log*

## Django ##
venv
venv*/
.venv
.venv*/
*.pyc
__pycache__/
staticfiles
mediafiles
.env
Expand Down
7 changes: 1 addition & 6 deletions apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { useState, useRef } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { HelpCircle, MessageSquare, MoveLeft } from "lucide-react";
import { HelpCircle, MoveLeft } from "lucide-react";
import { Transition } from "@headlessui/react";
import { WEB_BASE_URL } from "@plane/constants";
// plane internal packages
Expand All @@ -24,11 +24,6 @@ const helpOptions = [
href: "https://docs.plane.so/",
Icon: PageIcon,
},
{
name: "Join our Forum",
href: "https://forum.plane.so",
Icon: MessageSquare,
},
{
name: "Report a bug",
href: "https://github.com/makeplane/plane/issues/new/choose",
Expand Down
2 changes: 2 additions & 0 deletions apps/api/plane/api/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .work_item import urlpatterns as work_item_patterns
from .invite import urlpatterns as invite_patterns
from .sticky import urlpatterns as sticky_patterns
from .page import urlpatterns as page_patterns

urlpatterns = [
*asset_patterns,
Expand All @@ -28,4 +29,5 @@
*work_item_patterns,
*invite_patterns,
*sticky_patterns,
*page_patterns,
]
26 changes: 26 additions & 0 deletions apps/api/plane/api/urls/page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (c) 2023-present Plane Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.

from django.urls import path

from plane.api.views.page import PageViewSetV1

urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/",
PageViewSetV1.as_view({"get": "list", "post": "create"}),
name="project-pages-v1",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/pages/<uuid:pk>/",
PageViewSetV1.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="project-pages-detail-v1",
),
]
12 changes: 12 additions & 0 deletions apps/api/plane/api/views/page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2023-present Plane Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.

from plane.api.middleware.api_authentication import APIKeyAuthentication
from plane.api.rate_limit import ApiKeyRateThrottle
from plane.app.views.page.base import PageViewSet


class PageViewSetV1(PageViewSet):
authentication_classes = [APIKeyAuthentication]
throttle_classes = [ApiKeyRateThrottle]
11 changes: 11 additions & 0 deletions apps/api/plane/app/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@
CycleWriteSerializer,
CycleUserPropertiesSerializer,
)
from .sprint import (
WorkspaceSprintSerializer,
WorkspaceSprintAutomationSerializer,
WorkspaceSprintAutomationMemberSerializer,
WorkspaceSprintAutomationWriteSerializer,
WorkspaceSprintSquadSerializer,
WorkspaceSprintSquadMemberSerializer,
WorkspaceSprintSquadWriteSerializer,
WorkspaceSprintIssueSerializer,
WorkspaceSprintWriteSerializer,
)
from .asset import FileAssetSerializer
from .issue import (
IssueCreateSerializer,
Expand Down
6 changes: 6 additions & 0 deletions apps/api/plane/app/serializers/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,8 @@ class Meta:
class IssueSerializer(DynamicBaseSerializer):
# ids
cycle_id = serializers.PrimaryKeyRelatedField(read_only=True)
global_sprint_id = serializers.PrimaryKeyRelatedField(read_only=True)
global_sprint_name = serializers.CharField(read_only=True)
Comment on lines +764 to +765

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify serializer field type and annotation source shape.
rg -nP 'global_sprint_id\s*=\s*serializers\.PrimaryKeyRelatedField' apps/api/plane/app/serializers/issue.py
rg -nP 'global_sprint_id\s*=\s*Subquery\(' apps/api/plane/app/views/issue/base.py
rg -nP 'values\(\s*"sprint_id"\s*\)' apps/api/plane/app/views/issue/base.py

Repository: makeplane/plane

Length of output: 231


🏁 Script executed:

# Get context around the Subquery definitions at lines 119 and 239 in base.py
sed -n '115,130p' apps/api/plane/app/views/issue/base.py
sed -n '235,250p' apps/api/plane/app/views/issue/base.py

# Check what's at lines 793-794 in the serializer
sed -n '791,796p' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 1587


🏁 Script executed:

# Get clearer view of lines 793-796 in serializer
sed -n '790,800p' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 363


🏁 Script executed:

# Search for all definitions of global_sprint_id and global_sprint_name field definitions
rg -n 'global_sprint_id\s*=\s*serializers\.' apps/api/plane/app/serializers/issue.py
rg -n 'global_sprint_name\s*=\s*serializers\.' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 204


🏁 Script executed:

# Find all class definitions in the serializer file and their line numbers
rg -n '^class ' apps/api/plane/app/serializers/issue.py | head -20

# Show context around line 793
sed -n '780,810p' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 1864


🏁 Script executed:

# Get more context to see which class the fields tuple belongs to and check around 793-794
sed -n '750,815p' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 2142


🏁 Script executed:

# Search for all classes and look for ones that might have global_sprint_id definitions
rg -n '^class ' apps/api/plane/app/serializers/issue.py | tail -20

# Get file total line count
wc -l apps/api/plane/app/serializers/issue.py

# Search for all global_sprint_id field definitions (not in fields lists)
rg -B2 'global_sprint_id\s*=\s*serializers\.' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 1336


🏁 Script executed:

# Check lines 793-794 more carefully with line numbers
sed -n '791,800p' apps/api/plane/app/serializers/issue.py | cat -n

# Check if there are other serializer classes that might have global_sprint_id fields
rg -A5 'class.*Serializer.*:' apps/api/plane/app/serializers/issue.py | grep -A5 'global_sprint_id'

# Double check the exact location - show lines 819 onwards to see IssueListDetailSerializer
sed -n '819,860p' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 2591


🏁 Script executed:

# Check if IssueDetailSerializer also has global_sprint_id definition (it inherits from IssueSerializer)
sed -n '931,945p' apps/api/plane/app/serializers/issue.py

# Check IssueListDetailSerializer more - does it define global_sprint_id as a field?
sed -n '819,925p' apps/api/plane/app/serializers/issue.py | grep -n 'global_sprint_id'

# Search for any other global_sprint_id definitions
rg -n 'global_sprint_id.*=' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 693


🏁 Script executed:

# Check exact lines 793-794
sed -n '793,794p' apps/api/plane/app/serializers/issue.py

# Also check context around those lines
sed -n '790,800p' apps/api/plane/app/serializers/issue.py | cat -n

Repository: makeplane/plane

Length of output: 506


🏁 Script executed:

# Let's find all field definitions with their line numbers for global_sprint_id
rg -n 'global_sprint_id\s*=' apps/api/plane/app/serializers/issue.py

# Check if IssueLiteSerializer or IssueFlatSerializer have these fields
sed -n '52,70p' apps/api/plane/app/serializers/issue.py
sed -n '924,930p' apps/api/plane/app/serializers/issue.py

Repository: makeplane/plane

Length of output: 715


🏁 Script executed:

# Let's understand what type sprint_id is in the model
rg -n 'class WorkspaceSprintIssue' apps/api/plane/app/models/

# Find the model file
fd 'models\.py' apps/api/plane/app/models/ | head -5

# Check what sprint_id field type is
rg -B5 -A5 'sprint_id.*=' apps/api/plane/app/models/

Repository: makeplane/plane

Length of output: 307


🏁 Script executed:

# Find where models are located
fd 'models' apps/api/plane/app/ -type d

# Search for WorkspaceSprintIssue model
rg -l 'class WorkspaceSprintIssue' apps/api/

Repository: makeplane/plane

Length of output: 369


🏁 Script executed:

# Check the WorkspaceSprintIssue model definition
rg -B5 -A15 'class WorkspaceSprintIssue' apps/api/plane/db/models/sprint.py

# Also check if sprint_id is a UUID field
rg -n 'sprint_id' apps/api/plane/db/models/sprint.py | head -10

Repository: makeplane/plane

Length of output: 1016


🏁 Script executed:

# Check the WorkspaceSprint model to confirm sprint_id is UUID
rg -B2 -A10 'class WorkspaceSprint' apps/api/plane/db/models/sprint.py | head -20

# Verify the annotation is truly providing scalar values by checking the actual queryset call
sed -n '115,130p' apps/api/plane/app/views/issue/base.py
sed -n '235,250p' apps/api/plane/app/views/issue/base.py

Repository: makeplane/plane

Length of output: 2155


🏁 Script executed:

# Verify WorkspaceSprint model has id as UUID by checking the parent class
rg -B5 'class WorkspaceBaseModel' apps/api/plane/db/models/

# Check what type of ID field WorkspaceBaseModel uses
rg -A20 'class WorkspaceBaseModel' apps/api/plane/db/models/base.py

Repository: makeplane/plane

Length of output: 437


🏁 Script executed:

# Check the BaseModel class to understand the ID field type
rg -A30 'class BaseModel' apps/api/plane/db/models/base.py | head -40

Repository: makeplane/plane

Length of output: 1242


Use UUIDField for annotated global_sprint_id value.

Line 764 defines global_sprint_id as PrimaryKeyRelatedField, but the queryset annotation in apps/api/plane/app/views/issue/base.py provides a scalar UUID via values("sprint_id") within a Subquery, not a related model instance. This type mismatch can cause serialization errors on issue list responses.

Suggested fix
-    global_sprint_id = serializers.PrimaryKeyRelatedField(read_only=True)
+    global_sprint_id = serializers.UUIDField(read_only=True, allow_null=True)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
global_sprint_id = serializers.PrimaryKeyRelatedField(read_only=True)
global_sprint_name = serializers.CharField(read_only=True)
global_sprint_id = serializers.UUIDField(read_only=True, allow_null=True)
global_sprint_name = serializers.CharField(read_only=True)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/app/serializers/issue.py` around lines 764 - 765, The
global_sprint_id field in the IssueSerializer is incorrectly defined as a
PrimaryKeyRelatedField, but the queryset annotation provides a scalar UUID value
rather than a related model instance. Change the global_sprint_id field
definition from PrimaryKeyRelatedField to UUIDField with read_only=True to match
the actual data type being provided by the annotated queryset. This will resolve
the serialization type mismatch that causes errors in issue list responses.

module_ids = serializers.ListField(child=serializers.UUIDField(), required=False)

# Many to many
Expand Down Expand Up @@ -788,6 +790,8 @@ class Meta:
"project_id",
"parent_id",
"cycle_id",
"global_sprint_id",
"global_sprint_name",
"module_ids",
"label_ids",
"assignee_ids",
Expand Down Expand Up @@ -852,6 +856,8 @@ def to_representation(self, instance):
"archived_at": instance.archived_at,
# Computed fields
"cycle_id": instance.cycle_id,
"global_sprint_id": getattr(instance, "global_sprint_id", None),
"global_sprint_name": getattr(instance, "global_sprint_name", None),
"module_ids": self.get_module_ids(instance),
"label_ids": self.get_label_ids(instance),
"assignee_ids": self.get_assignee_ids(instance),
Expand Down
Loading