mirror of
https://github.com/makeplane/plane.git
synced 2025-12-19 03:40:09 -06:00
[WEB-5647] chore: list layout work item identifier enhancements (#8326)
This commit is contained in:
committed by
GitHub
parent
1b427392c4
commit
2ac5efe2f0
@@ -3,6 +3,7 @@ from rest_framework import serializers
|
||||
|
||||
# Module imports
|
||||
from .base import BaseSerializer, DynamicBaseSerializer
|
||||
from django.db.models import Max
|
||||
from plane.app.serializers.workspace import WorkspaceLiteSerializer
|
||||
from plane.app.serializers.user import UserLiteSerializer, UserAdminLiteSerializer
|
||||
from plane.db.models import (
|
||||
@@ -12,6 +13,7 @@ from plane.db.models import (
|
||||
ProjectIdentifier,
|
||||
DeployBoard,
|
||||
ProjectPublicMember,
|
||||
IssueSequence
|
||||
)
|
||||
from plane.utils.content_validator import (
|
||||
validate_html_content,
|
||||
@@ -105,6 +107,7 @@ class ProjectListSerializer(DynamicBaseSerializer):
|
||||
members = serializers.SerializerMethodField()
|
||||
cover_image_url = serializers.CharField(read_only=True)
|
||||
inbox_view = serializers.BooleanField(read_only=True, source="intake_view")
|
||||
next_work_item_sequence = serializers.SerializerMethodField()
|
||||
|
||||
def get_members(self, obj):
|
||||
project_members = getattr(obj, "members_list", None)
|
||||
@@ -113,6 +116,11 @@ class ProjectListSerializer(DynamicBaseSerializer):
|
||||
return [member.member_id for member in project_members if member.is_active and not member.member.is_bot]
|
||||
return []
|
||||
|
||||
def get_next_work_item_sequence(self, obj):
|
||||
"""Get the next sequence ID that will be assigned to a new issue"""
|
||||
max_sequence = IssueSequence.objects.filter(project_id=obj.id).aggregate(max_seq=Max("sequence"))["max_seq"]
|
||||
return (max_sequence + 1) if max_sequence else 1
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
fields = "__all__"
|
||||
|
||||
@@ -28,6 +28,7 @@ import { IssueIdentifier } from "@/plane-web/components/issues/issue-details/iss
|
||||
import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats";
|
||||
// types
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
import { calculateIdentifierWidth } from "../utils";
|
||||
import type { TRenderQuickActions } from "./list-view-types";
|
||||
|
||||
interface IssueBlockProps {
|
||||
@@ -76,7 +77,7 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
const projectId = routerProjectId?.toString();
|
||||
// hooks
|
||||
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
|
||||
const { getProjectIdentifierById } = useProject();
|
||||
const { getProjectIdentifierById, currentProjectNextSequenceId } = useProject();
|
||||
const {
|
||||
getIsIssuePeeked,
|
||||
peekIssue,
|
||||
@@ -150,8 +151,12 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier
|
||||
const keyMinWidth = displayProperties?.key ? (projectIdentifier?.length ?? 0) * 7 : 0;
|
||||
// Calculate width for: projectIdentifier + "-" + dynamic sequence number digits
|
||||
// Use next_work_item_sequence from backend (static value from project endpoint)
|
||||
const maxSequenceId = currentProjectNextSequenceId ?? 1;
|
||||
const keyMinWidth = displayProperties?.key
|
||||
? calculateIdentifierWidth(projectIdentifier?.length ?? 0, maxSequenceId)
|
||||
: 0;
|
||||
|
||||
const workItemLink = generateWorkItemLink({
|
||||
workspaceSlug,
|
||||
|
||||
@@ -748,3 +748,18 @@ export const isFiltersApplied = (filters: IIssueFilterOptions): boolean =>
|
||||
if (Array.isArray(value)) return value.length > 0;
|
||||
return value !== undefined && value !== null && value !== "";
|
||||
});
|
||||
|
||||
/**
|
||||
* Calculates the minimum width needed for issue identifiers in list layouts
|
||||
* @param projectIdentifierLength - Length of the project identifier (e.g., "PROJ" = 4)
|
||||
* @param maxSequenceId - Maximum sequence ID in the project (e.g., 1234)
|
||||
* @returns Width in pixels needed to display the identifier
|
||||
*
|
||||
* @example
|
||||
* // For "PROJ-1234"
|
||||
* calculateIdentifierWidth(4, 1234) // Returns width for "PROJ" + "-" + "1234"
|
||||
*/
|
||||
export const calculateIdentifierWidth = (projectIdentifierLength: number, maxSequenceId: number): number => {
|
||||
const sequenceDigits = Math.max(1, Math.floor(Math.log10(maxSequenceId)) + 1);
|
||||
return projectIdentifierLength * 7 + 7 + sequenceDigits * 7; // project identifier chars + dash + sequence digits
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface IProjectStore {
|
||||
joinedProjectIds: string[];
|
||||
favoriteProjectIds: string[];
|
||||
currentProjectDetails: TProject | undefined;
|
||||
currentProjectNextSequenceId: number | undefined;
|
||||
// actions
|
||||
getProjectById: (projectId: string | undefined | null) => TProject | undefined;
|
||||
getPartialProjectById: (projectId: string | undefined | null) => TPartialProject | undefined;
|
||||
@@ -107,6 +108,7 @@ export class ProjectStore implements IProjectStore {
|
||||
currentProjectDetails: computed,
|
||||
joinedProjectIds: computed,
|
||||
favoriteProjectIds: computed,
|
||||
currentProjectNextSequenceId: computed,
|
||||
// helper actions
|
||||
processProjectAfterCreation: action,
|
||||
// fetch actions
|
||||
@@ -216,6 +218,15 @@ export class ProjectStore implements IProjectStore {
|
||||
return this.projectMap?.[this.rootStore.router.projectId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next sequence ID for the current project
|
||||
* Used for calculating identifier width in list layouts
|
||||
*/
|
||||
get currentProjectNextSequenceId() {
|
||||
if (!this.rootStore.router.projectId) return undefined;
|
||||
return this.currentProjectDetails?.next_work_item_sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns joined project IDs belong to the current workspace
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface IProject extends IPartialProject {
|
||||
is_favorite?: boolean;
|
||||
members?: string[];
|
||||
timezone?: string;
|
||||
next_work_item_sequence?: number;
|
||||
}
|
||||
|
||||
export type TProjectAnalyticsCountParams = {
|
||||
|
||||
Reference in New Issue
Block a user