diff --git a/apps/api/plane/app/views/project/base.py b/apps/api/plane/app/views/project/base.py index 831a839a6d..348c821916 100644 --- a/apps/api/plane/app/views/project/base.py +++ b/apps/api/plane/app/views/project/base.py @@ -434,145 +434,128 @@ class ProjectViewSet(BaseViewSet): user_id=serializer.data["project_lead"], ) - if serializer.data["project_lead"] is not None and str( - serializer.data["project_lead"] - ) != str(request.user.id): - ProjectMember.objects.create( - project_id=serializer.data["id"], - member_id=serializer.data["project_lead"], - role=20, - ) - # Also create the issue property for the user - IssueUserProperty.objects.create( - project_id=serializer.data["id"], - user_id=serializer.data["project_lead"], - ) + # Default states + states = [ + { + "name": "Backlog", + "color": "#60646C", + "sequence": 15000, + "group": "backlog", + "default": True, + }, + { + "name": "Todo", + "color": "#60646C", + "sequence": 25000, + "group": "unstarted", + }, + { + "name": "In Progress", + "color": "#F59E0B", + "sequence": 35000, + "group": "started", + }, + { + "name": "Done", + "color": "#46A758", + "sequence": 45000, + "group": "completed", + }, + { + "name": "Cancelled", + "color": "#9AA4BC", + "sequence": 55000, + "group": "cancelled", + }, + ] - # Default states - states = [ - { - "name": "Backlog", - "color": "#60646C", - "sequence": 15000, - "group": "backlog", - "default": True, - }, - { - "name": "Todo", - "color": "#60646C", - "sequence": 25000, - "group": "unstarted", - }, - { - "name": "In Progress", - "color": "#F59E0B", - "sequence": 35000, - "group": "started", - }, - { - "name": "Done", - "color": "#46A758", - "sequence": 45000, - "group": "completed", - }, - { - "name": "Cancelled", - "color": "#9AA4BC", - "sequence": 55000, - "group": "cancelled", - }, + State.objects.bulk_create( + [ + State( + name=state["name"], + color=state["color"], + project=serializer.instance, + sequence=state["sequence"], + workspace=serializer.instance.workspace, + group=state["group"], + default=state.get("default", False), + created_by=request.user, + ) + for state in states ] + ) - State.objects.bulk_create( - [ - State( - name=state["name"], - color=state["color"], - project=serializer.instance, - sequence=state["sequence"], - workspace=serializer.instance.workspace, - group=state["group"], - default=state.get("default", False), - created_by=request.user, - ) - for state in states - ] - ) - - # validating the PROJECT_GROUPING feature flag is enabled - if check_workspace_feature_flag( - feature_key=FeatureFlag.PROJECT_GROUPING, - slug=slug, - user_id=str(request.user.id), - default_value=False, + # validating the PROJECT_GROUPING feature flag is enabled + if check_workspace_feature_flag( + feature_key=FeatureFlag.PROJECT_GROUPING, + slug=slug, + user_id=str(request.user.id), + default_value=False, + ): + # validating the is_project_grouping_enabled workspace feature is enabled + if check_workspace_feature( + slug, WorkspaceFeatureContext.IS_PROJECT_GROUPING_ENABLED ): - # validating the is_project_grouping_enabled workspace feature is enabled - if check_workspace_feature( - slug, WorkspaceFeatureContext.IS_PROJECT_GROUPING_ENABLED - ): - state_id = request.data.get("state_id", None) - priority = request.data.get("priority", "none") - start_date = request.data.get("start_date", None) - target_date = request.data.get("target_date", None) + state_id = request.data.get("state_id", None) + priority = request.data.get("priority", "none") + start_date = request.data.get("start_date", None) + target_date = request.data.get("target_date", None) - if state_id is None: - state_id = ( - ProjectState.objects.filter( - workspace=workspace, default=True - ) - .values_list("id", flat=True) - .first() + if state_id is None: + state_id = ( + ProjectState.objects.filter( + workspace=workspace, default=True ) - - # also create project attributes - _ = ProjectAttribute.objects.create( - project_id=serializer.data.get("id"), - state_id=state_id, - priority=priority, - start_date=start_date, - target_date=target_date, - workspace_id=workspace.id, + .values_list("id", flat=True) + .first() ) - project = ( - self.get_queryset().filter(pk=serializer.data["id"]).first() - ) + # also create project attributes + _ = ProjectAttribute.objects.create( + project_id=serializer.data.get("id"), + state_id=state_id, + priority=priority, + start_date=start_date, + target_date=target_date, + workspace_id=workspace.id, + ) - # Create the project feature - _ = ProjectFeature.objects.create( - workspace_id=workspace.id, project_id=project.id - ) + project = self.get_queryset().filter(pk=serializer.data["id"]).first() - # Create the model activity - model_activity.delay( - model_name="project", - model_id=str(project.id), - requested_data=request.data, - current_instance=None, - actor_id=request.user.id, - slug=slug, - origin=base_host(request=request, is_app=True), - ) + # Create the project feature + _ = ProjectFeature.objects.create( + workspace_id=workspace.id, project_id=project.id + ) - project_activity.delay( - type="project.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), - actor_id=str(request.user.id), - project_id=str(project.id), - current_instance=None, - epoch=int(timezone.now().timestamp()), - notification=True, - origin=request.META.get("HTTP_ORIGIN"), - ) + # Create the model activity + model_activity.delay( + model_name="project", + model_id=str(project.id), + requested_data=request.data, + current_instance=None, + actor_id=request.user.id, + slug=slug, + origin=base_host(request=request, is_app=True), + ) + + project_activity.delay( + type="project.activity.created", + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), + actor_id=str(request.user.id), + project_id=str(project.id), + current_instance=None, + epoch=int(timezone.now().timestamp()), + notification=True, + origin=request.META.get("HTTP_ORIGIN"), + ) + + serializer = ProjectListSerializer( + project, context={"request": request, "slug": slug} + ) + payload = self.update_project_member_role(serializer.data) + return Response(payload, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - serializer = ProjectListSerializer( - project, context={"request": request, "slug": slug} - ) - payload = self.update_project_member_role(serializer.data) - return Response(payload, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError as e: if "already exists" in str(e): return Response( diff --git a/apps/web/ce/components/projects/create/root.tsx b/apps/web/ce/components/projects/create/root.tsx index aeb8b3aaee..71935fa2e4 100644 --- a/apps/web/ce/components/projects/create/root.tsx +++ b/apps/web/ce/components/projects/create/root.tsx @@ -99,45 +99,32 @@ export const CreateProjectForm: FC = observer((props) = // Handle the new error format where codes are nested in arrays under field names const errorData = err?.data ?? {}; - // Check for specific error codes in the new format - if (errorData.name?.includes("PROJECT_NAME_ALREADY_EXIST")) { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("toast.error"), - message: t("project_name_already_taken"), - }); - } + const nameError = errorData.name?.includes("PROJECT_NAME_ALREADY_EXIST"); + const identifierError = errorData?.identifier?.includes("PROJECT_IDENTIFIER_ALREADY_EXIST"); - if (errorData?.identifier?.includes("PROJECT_IDENTIFIER_ALREADY_EXIST")) { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("toast.error"), - message: t("project_identifier_already_taken"), - }); - } - - // Handle other field-specific errors (excluding name and identifier which are handled above) - Object.keys(errorData).forEach((field) => { - // Skip name and identifier fields as they're handled separately above - if (field === "name" || field === "identifier") return; - - const fieldErrors = errorData[field]; - if (Array.isArray(fieldErrors)) { - fieldErrors.forEach((errorMessage) => { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("error"), - message: errorMessage, - }); - }); - } else if (typeof fieldErrors === "string") { + if (nameError || identifierError) { + if (nameError) { setToast({ type: TOAST_TYPE.ERROR, - title: t("error"), - message: fieldErrors, + title: t("toast.error"), + message: t("project_name_already_taken"), }); } - }); + + if (identifierError) { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("project_identifier_already_taken"), + }); + } + } else { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("something_went_wrong"), + }); + } } catch (error) { // Fallback error handling if the error processing fails console.error("Error processing API error:", error); diff --git a/apps/web/core/components/project/form.tsx b/apps/web/core/components/project/form.tsx index c93a316d84..4526ce8778 100644 --- a/apps/web/core/components/project/form.tsx +++ b/apps/web/core/components/project/form.tsx @@ -116,45 +116,34 @@ export const ProjectDetailsForm: FC = (props) => { // Handle the new error format where codes are nested in arrays under field names const errorData = err ?? {}; - // Check for specific error codes in the new format - if (errorData.name?.includes("PROJECT_NAME_ALREADY_EXIST")) { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("toast.error"), - message: t("project_name_already_taken"), - }); - } + const nameError = errorData.name?.includes("PROJECT_NAME_ALREADY_EXIST"); + const identifierError = errorData?.identifier?.includes("PROJECT_IDENTIFIER_ALREADY_EXIST"); - if (errorData?.identifier?.includes("PROJECT_IDENTIFIER_ALREADY_EXIST")) { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("toast.error"), - message: t("project_identifier_already_taken"), - }); - } + if (nameError || identifierError) { + console.log("coming here 1"); - // Handle other field-specific errors (excluding name and identifier which are handled above) - Object.keys(errorData).forEach((field) => { - // Skip name and identifier fields as they're handled separately above - if (field === "name" || field === "identifier") return; - - const fieldErrors = errorData[field]; - if (Array.isArray(fieldErrors)) { - fieldErrors.forEach((errorMessage) => { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("error"), - message: errorMessage, - }); - }); - } else if (typeof fieldErrors === "string") { + if (nameError) { setToast({ type: TOAST_TYPE.ERROR, - title: t("error"), - message: fieldErrors, + title: t("toast.error"), + message: t("project_name_already_taken"), }); } - }); + + if (identifierError) { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("project_identifier_already_taken"), + }); + } + } else { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("something_went_wrong"), + }); + } } catch (error) { // Fallback error handling if the error processing fails console.error("Error processing API error:", error); diff --git a/apps/web/ee/components/projects/create/root.tsx b/apps/web/ee/components/projects/create/root.tsx index 934fb39507..010aac23f8 100644 --- a/apps/web/ee/components/projects/create/root.tsx +++ b/apps/web/ee/components/projects/create/root.tsx @@ -156,19 +156,52 @@ export const CreateProjectFormBase: FC = observer((prop } }) .catch((err) => { - Object.keys(err?.data ?? {}).map((key) => { - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: err.data[key], - }); + try { captureError({ eventName: PROJECT_TRACKER_EVENTS.create, payload: { identifier: formData.identifier, }, }); - }); + + // Handle the new error format where codes are nested in arrays under field names + const errorData = err?.data ?? {}; + + const nameError = errorData.name?.includes("PROJECT_NAME_ALREADY_EXIST"); + const identifierError = errorData?.identifier?.includes("PROJECT_IDENTIFIER_ALREADY_EXIST"); + + if (nameError || identifierError) { + if (nameError) { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("project_name_already_taken"), + }); + } + + if (identifierError) { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("project_identifier_already_taken"), + }); + } + } else { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("something_went_wrong"), + }); + } + } catch (error) { + // Fallback error handling if the error processing fails + console.error("Error processing API error:", error); + setToast({ + type: TOAST_TYPE.ERROR, + title: t("toast.error"), + message: t("something_went_wrong"), + }); + } }); };