[WEB-5568] feat: Allow Multiple Assignees for Intake Responsibility (#4970)

This commit is contained in:
b-saikrishnakanth
2025-12-04 18:10:08 +05:30
committed by GitHub
parent 81435f220c
commit cf58d7485d
27 changed files with 187 additions and 398 deletions
+78 -29
View File
@@ -12,8 +12,12 @@ from plane.ee.models import (
IntakeFormField,
IntakeResponsibility,
IntakeResponsibilityTypeChoices,
TeamspaceMember,
TeamspaceProject,
)
from plane.db.models import Project
from plane.db.models import Project, ProjectMember
from plane.payment.flags.flag_decorator import check_workspace_feature_flag
from plane.payment.flags.flag import FeatureFlag
class IntakeSettingSerializer(BaseSerializer):
@@ -29,44 +33,89 @@ class IntakeSettingSerializer(BaseSerializer):
]
class IntakeResponsibilitySerializer(BaseSerializer):
class Meta:
model = IntakeResponsibility
fields = [
"id",
"intake",
"user",
"type",
"project",
]
read_only_fields = [
"id",
"intake",
"project",
]
class IntakeResponsibilitySerializer(serializers.Serializer):
users = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True)
def validate_users(self, value):
if not value:
return []
project_id = self.context.get("project_id")
if not project_id:
raise serializers.ValidationError("Project is required")
# Check if teamspace projects is enabled
if check_workspace_feature_flag(
feature_key=FeatureFlag.TEAMSPACES, user_id=self.context.get("user_id"), slug=self.context.get("slug")
):
# Get the project members and teamspace members
teamspace_ids = TeamspaceProject.objects.filter(project_id=project_id).values_list(
"team_space_id", flat=True
)
return list(
ProjectMember.objects.filter(project_id=project_id, member_id__in=value, is_active=True).values_list(
"member_id", flat=True
)
) + list(
TeamspaceMember.objects.filter(team_space_id__in=teamspace_ids, member_id__in=value).values_list(
"member_id", flat=True
)
)
return list(
ProjectMember.objects.filter(project_id=project_id, member_id__in=value, is_active=True).values_list(
"member_id", flat=True
)
)
def create(self, validated_data):
# Remove the existing responsibility and add the new user
intake = self.context.get("intake")
project_id = self.context.get("project_id")
if not intake or not project_id:
raise serializers.ValidationError("Intake and project are required")
# Check if there is an existing intake responsibility for this intake and project
intake_responsibility = IntakeResponsibility.objects.filter(intake=intake, project_id=project_id).first()
# Get the existing users
existing_users = IntakeResponsibility.objects.filter(intake=intake, project_id=project_id).values_list(
"user_id", flat=True
)
requested_users = validated_data.get("users")
if intake_responsibility:
intake_responsibility.user = validated_data.get("user")
intake_responsibility.type = validated_data.get("type", IntakeResponsibilityTypeChoices.ASSIGNEE)
intake_responsibility.save()
else:
intake_responsibility = IntakeResponsibility.objects.create(
intake=intake,
project_id=project_id,
user=validated_data.get("user"),
type=validated_data.get("type", IntakeResponsibilityTypeChoices.ASSIGNEE),
new_users = set(requested_users) - set(existing_users)
deleted_users = set(existing_users) - set(requested_users)
with transaction.atomic():
# Delete the existing users
IntakeResponsibility.objects.filter(
intake=intake, project_id=project_id, user_id__in=deleted_users
).delete()
# Create the new users
IntakeResponsibility.objects.bulk_create(
[
IntakeResponsibility(
intake=intake,
project_id=project_id,
user_id=user_id,
workspace_id=intake.workspace_id,
type=IntakeResponsibilityTypeChoices.ASSIGNEE,
)
for user_id in new_users
],
batch_size=10,
)
return intake_responsibility
# Return all requested users (the final state), not just newly created ones
return requested_users
def to_representation(self, instance):
"""
Convert the list of user IDs to the expected format
"""
if isinstance(instance, list):
# Convert user_ids to strings
return {"users": [str(user_id) for user_id in instance]}
return {"users": []}
class IntakeFormSerializer(BaseSerializer):
@@ -11,11 +11,11 @@ from plane.ee.models import IntakeResponsibility
from plane.payment.flags.flag_decorator import check_feature_flag
from plane.payment.flags.flag import FeatureFlag
class IntakeResponsibilityEndpoint(BaseAPIView):
serializer_class = IntakeResponsibilitySerializer
model = IntakeResponsibility
@allow_permission([ROLE.ADMIN], level="PROJECT")
@check_feature_flag(FeatureFlag.INTAKE_RESPONSIBILITY)
def post(self, request, slug, project_id):
@@ -26,7 +26,8 @@ class IntakeResponsibilityEndpoint(BaseAPIView):
status=status.HTTP_404_NOT_FOUND,
)
serializer = IntakeResponsibilitySerializer(
data=request.data, context={"intake": intake, "project_id": project_id}
data=request.data,
context={"intake": intake, "project_id": project_id, "user_id": request.user.id, "slug": slug},
)
if serializer.is_valid(raise_exception=True):
serializer.save()
@@ -55,6 +56,6 @@ class IntakeResponsibilityEndpoint(BaseAPIView):
{"error": "Intake not found"},
status=status.HTTP_404_NOT_FOUND,
)
# Since right now we are only allowing one responsibility per intake, we can just get the first one and return it
responsibility = IntakeResponsibility.objects.filter(intake=intake).first()
return Response(IntakeResponsibilitySerializer(responsibility).data, status=status.HTTP_200_OK)
# Return all user IDs assigned responsibility for this intake
responsibility = IntakeResponsibility.objects.filter(intake=intake).values_list("user_id", flat=True)
return Response(responsibility, status=status.HTTP_200_OK)
@@ -1806,7 +1806,7 @@ def issue_activity(
actor_id=actor_id,
project_id=project_id,
subscriber=subscriber,
issue_activities_created=json.dumps(
activities_data=json.dumps(
IssueActivitySerializer(issue_activities_created, many=True).data,
cls=DjangoJSONEncoder,
),
@@ -29,14 +29,14 @@ const IntakeResponsibility = observer((props: Props) => {
// hooks
const { t } = useTranslation();
// store hooks
const { getAssignee, fetchIntakeAssignee, createIntakeAssignee, deleteIntakeAssignee } = useIntakeResponsibility();
const { getAssignees, fetchIntakeAssignees, updateIntakeAssignees } = useIntakeResponsibility();
const { togglePaidPlanModal } = useWorkspaceSubscription();
const assignee = projectId ? getAssignee(projectId) : null;
const assignees = projectId ? getAssignees(projectId) : [];
/**Fetch intake assignee */
useSWR(
workspaceSlug && projectId ? `INTAKE_ASSIGNEE_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => fetchIntakeAssignee(workspaceSlug.toString(), projectId.toString()) : null,
workspaceSlug && projectId ? () => fetchIntakeAssignees(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
);
@@ -47,33 +47,10 @@ const IntakeResponsibility = observer((props: Props) => {
const intakeT = (path: string) => t(`project_settings.features.intake.${path}`);
const handleAssigneeChange = async (val: string | null) => {
const handleAssigneeChange = async (val: string[]) => {
if (!workspaceSlug || !projectId) return;
const currentAssignee = assignee;
let updatePromise: Promise<void>;
// If clicking on the already selected assignee or clearing (setting to null), remove it
if (val === null || (val === currentAssignee && currentAssignee !== null)) {
if (!currentAssignee) return;
updatePromise = deleteIntakeAssignee(workspaceSlug, projectId, currentAssignee);
setPromiseToast(updatePromise, {
loading: intakeT("toasts.remove.loading"),
success: {
title: intakeT("toasts.remove.success.title"),
message: () => intakeT("toasts.remove.success.message"),
},
error: {
title: intakeT("toasts.remove.error.title"),
message: () => intakeT("toasts.remove.error.message"),
},
});
return;
}
// Create new assignee
updatePromise = createIntakeAssignee(workspaceSlug, projectId, { user: val });
const updatePromise = updateIntakeAssignees(workspaceSlug, projectId, { users: val });
setPromiseToast(updatePromise, {
loading: intakeT("toasts.set.loading"),
@@ -120,16 +97,16 @@ const IntakeResponsibility = observer((props: Props) => {
<div className="flex items-center h-8 max-w-40">
{isResponsibilityEnabled ? (
<MemberDropdown
value={assignee}
value={assignees}
onChange={handleAssigneeChange}
projectId={projectId}
placeholder={t("no_assignee")}
multiple={false}
multiple
showUserDetails
buttonVariant="border-with-text"
className="w-full"
buttonContainerClassName="w-full text-left"
buttonClassName={assignee ? "hover:bg-transparent" : ""}
buttonClassName={assignees.length > 0 ? "hover:bg-transparent" : ""}
dropdownArrow
/>
) : (
@@ -71,8 +71,8 @@ export const INTAKE_FEATURES_LIST: TIntakeFeatureList = {
export const INTAKE_RESPONSIBILITY_LIST: TIntakeResponsibilityList = {
notify_assignee: {
property: "notify_assignee",
title: "Notify assignee",
description: "For a new request to intake, default assignee will be alerted via notifications",
title: "Notify assignees",
description: "For a new request to intake, default assignees will be alerted via notifications",
icon: <Users className="h-4 w-4 flex-shrink-0 text-custom-text-300" />,
isPro: false,
isEnabled: true,
@@ -1,5 +1,4 @@
import { API_BASE_URL } from "@plane/constants";
import type { TIntakeUser } from "@plane/types";
// services
import { APIService } from "@/services/api.service";
@@ -8,7 +7,7 @@ export class IntakeResponsibilityService extends APIService {
super(API_BASE_URL);
}
async getIntakeAssignee(workspaceSlug: string, projectId: string): Promise<TIntakeUser> {
async getIntakeAssignees(workspaceSlug: string, projectId: string): Promise<string[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/intake-responsibilities/`)
.then((res) => res?.data)
.catch((err) => {
@@ -16,19 +15,15 @@ export class IntakeResponsibilityService extends APIService {
});
}
async createIntakeAssignee(workspaceSlug: string, projectId: string, data: { user: string }): Promise<TIntakeUser> {
async updateIntakeAssignees(
workspaceSlug: string,
projectId: string,
data: { users: string[] }
): Promise<{ users: string[] }> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/intake-responsibilities/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIntakeAssignee(workspaceSlug: string, projectId: string, userId: string): Promise<void> {
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/intake-responsibilities/${userId}/`)
.then((response) => response?.data)
.catch((err) => {
throw err?.response?.data;
});
}
}
@@ -1,24 +1,21 @@
import { action, makeObservable, observable, runInAction } from "mobx";
import { computedFn } from "mobx-utils";
// plane imports
import type { TIntakeUser } from "@plane/types";
// services
import { IntakeResponsibilityService } from "@/plane-web/services/intake-responsibility.service";
// store
import type { RootStore } from "@/plane-web/store/root.store";
export interface IIntakeResponsibilityStore {
assigneesMap: Map<string, TIntakeUser>;
fetchIntakeAssignee: (workspaceSlug: string, projectId: string) => Promise<void>;
createIntakeAssignee: (workspaceSlug: string, projectId: string, data: { user: string }) => Promise<void>;
deleteIntakeAssignee: (workspaceSlug: string, projectId: string, userId: string) => Promise<void>;
assigneesMap: Map<string, string[]>;
fetchIntakeAssignees: (workspaceSlug: string, projectId: string) => Promise<void>;
updateIntakeAssignees: (workspaceSlug: string, projectId: string, data: { users: string[] }) => Promise<void>;
// computed
getAssignee: (projectId: string) => string | null;
getAssignees: (projectId: string) => string[];
}
export class IntakeResponsibilityStore implements IIntakeResponsibilityStore {
// observables
assigneesMap: Map<string, TIntakeUser> = new Map();
assigneesMap: Map<string, string[]> = new Map();
// services
intakeResponsibilityService = new IntakeResponsibilityService();
@@ -31,19 +28,18 @@ export class IntakeResponsibilityStore implements IIntakeResponsibilityStore {
// observable
assigneesMap: observable,
// actions
fetchIntakeAssignee: action,
createIntakeAssignee: action,
deleteIntakeAssignee: action,
fetchIntakeAssignees: action,
updateIntakeAssignees: action,
});
this.rootStore = rootStore;
}
// actions
fetchIntakeAssignee = async (workspaceSlug: string, projectId: string) => {
fetchIntakeAssignees = async (workspaceSlug: string, projectId: string) => {
try {
const assignee = await this.intakeResponsibilityService.getIntakeAssignee(workspaceSlug, projectId);
const assignees = await this.intakeResponsibilityService.getIntakeAssignees(workspaceSlug, projectId);
runInAction(() => {
this.assigneesMap.set(projectId, assignee);
this.assigneesMap.set(projectId, assignees);
});
} catch (error) {
console.error("Error fetching intake assignee", error);
@@ -51,33 +47,21 @@ export class IntakeResponsibilityStore implements IIntakeResponsibilityStore {
}
};
createIntakeAssignee = async (workspaceSlug: string, projectId: string, data: { user: string }) => {
updateIntakeAssignees = async (workspaceSlug: string, projectId: string, data: { users: string[] }) => {
try {
const assignee = await this.intakeResponsibilityService.createIntakeAssignee(workspaceSlug, projectId, data);
const { users } = await this.intakeResponsibilityService.updateIntakeAssignees(workspaceSlug, projectId, data);
runInAction(() => {
this.assigneesMap.set(projectId, assignee);
this.assigneesMap.set(projectId, users);
});
} catch (error) {
console.error("Error creating intake assignee", error);
throw error;
}
};
deleteIntakeAssignee = async (workspaceSlug: string, projectId: string, userId: string) => {
try {
await this.intakeResponsibilityService.deleteIntakeAssignee(workspaceSlug, projectId, userId);
runInAction(() => {
this.assigneesMap.delete(projectId);
});
} catch (error) {
console.error("Error deleting intake assignee", error);
console.error("Error updating intake assignees", error);
throw error;
}
};
// computed
getAssignee = computedFn((projectId: string) => {
const assignee = this.assigneesMap.get(projectId);
return assignee?.user || null;
getAssignees = computedFn((projectId: string) => {
const assignees = this.assigneesMap.get(projectId);
return assignees || [];
});
}
@@ -166,30 +166,19 @@ export default {
intake: {
heading: "Odpovědnost za příjem",
notify_assignee: {
title: "Upozornit přiřazeného",
description: "Pro novou žádost o příjem bude výchozí přiřazený upozorněn prostřednictvím oznámení",
title: "Upozornit přiřazené",
description: "Pro novou žádost o příjem budou výchozí přiřazení upozorněni prostřednictvím oznámení",
},
toasts: {
remove: {
loading: "Odebírání přiřazeného...",
success: {
title: "Úspěch!",
message: "Přiřazený byl úspěšně odebrán.",
},
error: {
title: "Chyba!",
message: "Při odebírání přiřazeného došlo k chybě. Zkuste to prosím znovu.",
},
},
set: {
loading: "Nastavování přiřazeného...",
loading: "Nastavování přiřazených...",
success: {
title: "Úspěch!",
message: "Přiřazený byl úspěšně nastaven.",
message: "Přiřazení úspěšně nastaveno.",
},
error: {
title: "Chyba!",
message: "Při nastavování přiřazeného došlo k chybě. Zkuste to prosím znovu.",
message: "Při nastavování přiřazených se něco pokazilo. Zkuste to prosím znovu.",
},
},
},
@@ -167,30 +167,19 @@ export default {
intake: {
heading: "Intake-Verantwortung",
notify_assignee: {
title: "Zuständigen benachrichtigen",
description: "Für eine neue Intake-Anfrage wird der Standard-Zuständige über Benachrichtigungen informiert",
title: "Zuständige benachrichtigen",
description: "Für eine neue Intake-Anfrage werden die Standard-Zuständigen über Benachrichtigungen informiert",
},
toasts: {
remove: {
loading: "Zuständigen wird entfernt...",
success: {
title: "Erfolg!",
message: "Zuständiger erfolgreich entfernt.",
},
error: {
title: "Fehler!",
message: "Beim Entfernen des Zuständigen ist etwas schief gelaufen. Bitte versuchen Sie es erneut.",
},
},
set: {
loading: "Zuständiger wird festgelegt...",
loading: "Zuständige werden festgelegt...",
success: {
title: "Erfolg!",
message: "Zuständiger erfolgreich festgelegt.",
message: "Zuständige erfolgreich festgelegt.",
},
error: {
title: "Fehler!",
message: "Beim Festlegen des Zuständigen ist etwas schief gelaufen. Bitte versuchen Sie es erneut.",
message: "Beim Festlegen der Zuständigen ist etwas schiefgelaufen. Bitte versuchen Sie es erneut.",
},
},
},
@@ -233,30 +233,19 @@ export default {
intake: {
heading: "Intake responsibility",
notify_assignee: {
title: "Notify assignee",
description: "For a new request to intake, default assignee will be alerted via notifications",
title: "Notify assignees",
description: "For a new request to intake, default assignees will be alerted via notifications",
},
toasts: {
remove: {
loading: "Removing assignee...",
success: {
title: "Success!",
message: "Assignee removed successfully.",
},
error: {
title: "Error!",
message: "Something went wrong while removing assignee. Please try again.",
},
},
set: {
loading: "Setting assignee...",
loading: "Setting assignees...",
success: {
title: "Success!",
message: "Assignee set successfully.",
message: "Assignees set successfully.",
},
error: {
title: "Error!",
message: "Something went wrong while setting assignee. Please try again.",
message: "Something went wrong while setting assignees. Please try again.",
},
},
},
@@ -166,30 +166,19 @@ export default {
intake: {
heading: "Responsabilidad de ingreso",
notify_assignee: {
title: "Notificar asignado",
description: "Para una nueva solicitud de ingreso, el asignado predeterminado será alertado mediante notificaciones",
title: "Notificar asignados",
description: "Para una nueva solicitud de ingreso, los asignados predeterminados serán alertados mediante notificaciones",
},
toasts: {
remove: {
loading: "Eliminando asignado...",
success: {
title: "¡Éxito!",
message: "Asignado eliminado exitosamente.",
},
error: {
title: "¡Error!",
message: "Algo salió mal al eliminar el asignado. Por favor, inténtalo de nuevo.",
},
},
set: {
loading: "Estableciendo asignado...",
loading: "Estableciendo asignados...",
success: {
title: "¡Éxito!",
message: "Asignado establecido exitosamente.",
message: "Asignados establecidos correctamente.",
},
error: {
title: "¡Error!",
message: "Algo salió mal al establecer el asignado. Por favor, inténtalo de nuevo.",
message: "Algo salió mal al establecer los asignados. Por favor, inténtalo de nuevo.",
},
},
},
@@ -166,30 +166,19 @@ export default {
intake: {
heading: "Responsabilité d'ingestion",
notify_assignee: {
title: "Notifier le responsable",
description: "Pour une nouvelle demande d'ingestion, le responsable par défaut sera alerté via des notifications",
title: "Notifier les responsables",
description: "Pour une nouvelle demande d'ingestion, les responsables par défaut seront alertés via des notifications",
},
toasts: {
remove: {
loading: "Suppression du responsable...",
success: {
title: "Succès !",
message: "Responsable supprimé avec succès.",
},
error: {
title: "Erreur !",
message: "Une erreur s'est produite lors de la suppression du responsable. Veuillez réessayer.",
},
},
set: {
loading: "Définition du responsable...",
loading: "Définition des responsables...",
success: {
title: "Succès !",
message: "Responsable défini avec succès.",
message: "Responsables définis avec succès.",
},
error: {
title: "Erreur !",
message: "Une erreur s'est produite lors de la définition du responsable. Veuillez réessayer.",
message: "Une erreur s'est produite lors de la définition des responsables. Veuillez réessayer.",
},
},
},
@@ -171,17 +171,6 @@ export default {
description: "Untuk permintaan penerimaan baru, yang ditugaskan secara default akan diberi peringatan melalui notifikasi",
},
toasts: {
remove: {
loading: "Menghapus yang ditugaskan...",
success: {
title: "Berhasil!",
message: "Yang ditugaskan berhasil dihapus.",
},
error: {
title: "Kesalahan!",
message: "Terjadi kesalahan saat menghapus yang ditugaskan. Silakan coba lagi.",
},
},
set: {
loading: "Mengatur yang ditugaskan...",
success: {
@@ -165,30 +165,19 @@ export default {
intake: {
heading: "Responsabilità di accettazione",
notify_assignee: {
title: "Notifica assegnatario",
description: "Per una nuova richiesta di accettazione, l'assegnatario predefinito sarà avvisato tramite notifiche",
title: "Notifica assegnatari",
description: "Per una nuova richiesta di accettazione, gli assegnatari predefiniti saranno avvisati tramite notifiche",
},
toasts: {
remove: {
loading: "Rimozione assegnatario...",
success: {
title: "Successo!",
message: "Assegnatario rimosso con successo.",
},
error: {
title: "Errore!",
message: "Qualcosa è andato storto durante la rimozione dell'assegnatario. Riprova.",
},
},
set: {
loading: "Impostazione assegnatario...",
loading: "Impostazione degli assegnatari...",
success: {
title: "Successo!",
message: "Assegnatario impostato con successo.",
message: "Assegnatari impostati con successo.",
},
error: {
title: "Errore!",
message: "Qualcosa è andato storto durante l'impostazione dell'assegnatario. Riprova.",
message: "Qualcosa è andato storto durante l'impostazione degli assegnatari. Riprova.",
},
},
},
@@ -168,17 +168,6 @@ export default {
description: "新しい受付リクエストの場合、デフォルトの担当者が通知を通じてアラートを受け取ります",
},
toasts: {
remove: {
loading: "担当者を削除中...",
success: {
title: "成功!",
message: "担当者が正常に削除されました。",
},
error: {
title: "エラー!",
message: "担当者の削除中に問題が発生しました。もう一度お試しください。",
},
},
set: {
loading: "担当者を設定中...",
success: {
@@ -169,17 +169,6 @@ export default {
description: "새로운 인테이크 요청의 경우 기본 담당자가 알림을 통해 알림을 받습니다",
},
toasts: {
remove: {
loading: "담당자 제거 중...",
success: {
title: "성공!",
message: "담당자가 성공적으로 제거되었습니다.",
},
error: {
title: "오류!",
message: "담당자를 제거하는 중 문제가 발생했습니다. 다시 시도해 주세요.",
},
},
set: {
loading: "담당자 설정 중...",
success: {
@@ -188,7 +177,7 @@ export default {
},
error: {
title: "오류!",
message: "담당자 설정하는 중 문제가 발생했습니다. 다시 시도해 주세요.",
message: "담당자 설정 중 문제가 발생했습니다. 다시 시도해 주세요.",
},
},
},
@@ -166,30 +166,19 @@ export default {
intake: {
heading: "Odpowiedzialność za przyjęcie",
notify_assignee: {
title: "Powiadom przypisanego",
description: "Dla nowego żądania przyjęcia domyślny przypisany zostanie powiadomiony poprzez powiadomienia",
title: "Powiadom przypisanych",
description: "Dla nowego żądania przyjęcia domyślni przypisani zostaną powiadomieni poprzez powiadomienia",
},
toasts: {
remove: {
loading: "Usuwanie przypisanego...",
success: {
title: "Sukces!",
message: "Przypisany został pomyślnie usunięty.",
},
error: {
title: "Błąd!",
message: "Coś poszło nie tak podczas usuwania przypisanego. Proszę spróbować ponownie.",
},
},
set: {
loading: "Ustawianie przypisanego...",
loading: "Ustawianie przypisanych...",
success: {
title: "Sukces!",
message: "Przypisany został pomyślnie ustawiony.",
message: "Przypisani ustawieni pomyślnie.",
},
error: {
title: "Błąd!",
message: "Coś poszło nie tak podczas ustawiania przypisanego. Proszę spróbować ponownie.",
message: "Coś poszło nie tak podczas ustawiania przypisanych. Spróbuj ponownie.",
},
},
},
@@ -167,30 +167,19 @@ export default {
intake: {
heading: "Responsabilidade de recebimento",
notify_assignee: {
title: "Notificar responsável",
description: "Para uma nova solicitação de recebimento, o responsável padrão será alertado via notificações",
title: "Notificar responsáveis",
description: "Para uma nova solicitação de recebimento, os responsáveis padrão serão alertados via notificações",
},
toasts: {
remove: {
loading: "Removendo responsável...",
success: {
title: "Sucesso!",
message: "Responsável removido com sucesso.",
},
error: {
title: "Erro!",
message: "Algo deu errado ao remover o responsável. Por favor, tente novamente.",
},
},
set: {
loading: "Definindo responsável...",
loading: "Definindo responsáveis...",
success: {
title: "Sucesso!",
message: "Responsável definido com sucesso.",
message: "Responsáveis definidos com sucesso.",
},
error: {
title: "Erro!",
message: "Algo deu errado ao definir o responsável. Por favor, tente novamente.",
message: "Algo deu errado ao definir os responsáveis. Por favor, tente novamente.",
},
},
},
@@ -167,30 +167,19 @@ export default {
intake: {
heading: "Responsabilitate de primire",
notify_assignee: {
title: "Notifică persoana desemnată",
description: "Pentru o nouă cerere de primire, persoana desemnată implicit va fi alertată prin notificări",
title: "Notifică persoanele desemnate",
description: "Pentru o nouă cerere de primire, persoanele desemnate implicit vor fi alertate prin notificări",
},
toasts: {
remove: {
loading: "Eliminarea persoanei desemnate...",
success: {
title: "Succes!",
message: "Persoana desemnată a fost eliminată cu succes.",
},
error: {
title: "Eroare!",
message: "Ceva a mers greșit la eliminarea persoanei desemnate. Te rugăm să încerci din nou.",
},
},
set: {
loading: "Setarea persoanei desemnate...",
loading: "Setarea persoanelor desemnate...",
success: {
title: "Succes!",
message: "Persoana desemnată a fost setată cu succes.",
message: "Persoanele desemnate au fost setate cu succes.",
},
error: {
title: "Eroare!",
message: "Ceva a mers greșit la setarea persoanei desemnate. Te rugăm să încerci din nou.",
message: "Ceva a mers greșit la setarea persoanelor desemnate. rugăm să încerci din nou.",
},
},
},
@@ -160,30 +160,19 @@ export default {
intake: {
heading: "Ответственность за прием",
notify_assignee: {
title: "Уведомить ответственного",
description: "Для нового запроса на прием ответственный по умолчанию будет уведомлен через уведомления",
title: "Уведомить ответственных",
description: "Для нового запроса на прием ответственные по умолчанию будут уведомлены через уведомления",
},
toasts: {
remove: {
loading: "Удаление ответственного...",
success: {
title: "Успех!",
message: "Ответственный успешно удален.",
},
error: {
title: "Ошибка!",
message: "Что-то пошло не так при удалении ответственного. Пожалуйста, попробуйте снова.",
},
},
set: {
loading: "Установка ответственного...",
loading: "Установка ответственных...",
success: {
title: "Успех!",
message: "Ответственный успешно установлен.",
message: "Ответственные успешно установлены.",
},
error: {
title: "Ошибка!",
message: "Что-то пошло не так при установке ответственного. Пожалуйста, попробуйте снова.",
message: "Что-то пошло не так при установке ответственных. Пожалуйста, попробуйте снова.",
},
},
},
@@ -166,30 +166,19 @@ export default {
intake: {
heading: "Zodpovednosť za príjem",
notify_assignee: {
title: "Upozorniť priradeného",
description: "Pre novú žiadosť o príjem bude predvolený priradený upozornený prostredníctvom oznámení",
title: "Upozorniť priradených",
description: "Pre novú žiadosť o príjem budú predvolení priradení upozornení prostredníctvom oznámení",
},
toasts: {
remove: {
loading: "Odstraňovanie priradeného...",
success: {
title: "Úspech!",
message: "Priradený bol úspešne odstránený.",
},
error: {
title: "Chyba!",
message: "Pri odstraňovaní priradeného došlo k chybe. Skúste to prosím znova.",
},
},
set: {
loading: "Nastavovanie priradeného...",
loading: "Nastavovanie priradených...",
success: {
title: "Úspech!",
message: "Priradený bol úspešne nastavený.",
message: "Priradení úspešne nastavení.",
},
error: {
title: "Chyba!",
message: "Pri nastavovaní priradeného došlo k chybe. Skúste to prosím znova.",
message: "Pri nastavovaní priradených sa niečo pokazilo. Skúste to prosím znova.",
},
},
},
@@ -171,30 +171,19 @@ export default {
intake: {
heading: "Alım sorumluluğu",
notify_assignee: {
title: "Atanan kişiyi bildir",
description: "Yeni bir alım talebi için varsayılan atanan kişi bildirimler aracılığıyla uyarılacaktır",
title: "Atanan kişileri bildir",
description: "Yeni bir alım talebi için varsayılan atanan kişiler bildirimler aracılığıyla uyarılacaktır",
},
toasts: {
remove: {
loading: "Atanan kişi kaldırılıyor...",
success: {
title: "Başarılı!",
message: "Atanan kişi başarıyla kaldırıldı.",
},
error: {
title: "Hata!",
message: "Atanan kişiyi kaldırırken bir şeyler yanlış gitti. Lütfen tekrar deneyin.",
},
},
set: {
loading: "Atanan kişi ayarlanıyor...",
loading: "Atanan kişiler ayarlanıyor...",
success: {
title: "Başarılı!",
message: "Atanan kişi başarıyla ayarlandı.",
message: "Atanan kişiler başarıyla ayarlandı.",
},
error: {
title: "Hata!",
message: "Atanan kişiyi ayarlarken bir şeyler yanlış gitti. Lütfen tekrar deneyin.",
message: "Atanan kişileri ayarlarken bir şeyler yanlış gitti. Lütfen tekrar deneyin.",
},
},
},
@@ -166,30 +166,19 @@ export default {
intake: {
heading: "Відповідальність за прийом",
notify_assignee: {
title: "Сповістити призначеного",
description: "Для нового запиту на прийом призначений за замовчуванням буде сповіщений через сповіщення",
title: "Сповістити призначених",
description: "Для нового запиту на прийом призначені за замовчуванням будуть сповіщені через сповіщення",
},
toasts: {
remove: {
loading: "Видалення призначеного...",
success: {
title: "Успіх!",
message: "Призначеного успішно видалено.",
},
error: {
title: "Помилка!",
message: "Щось пішло не так під час видалення призначеного. Будь ласка, спробуйте ще раз.",
},
},
set: {
loading: "Встановлення призначеного...",
loading: "Встановлення призначених...",
success: {
title: "Успіх!",
message: "Призначеного успішно встановлено.",
message: "Призначені успішно встановлені.",
},
error: {
title: "Помилка!",
message: "Щось пішло не так під час встановлення призначеного. Будь ласка, спробуйте ще раз.",
message: "Щось пішло не так під час встановлення призначених. Будь ласка, спробуйте ще раз.",
},
},
},
@@ -159,22 +159,11 @@ export default {
description: "Đối với yêu cầu tiếp nhận mới, người được chỉ định mặc định sẽ được cảnh báo qua thông báo",
},
toasts: {
remove: {
loading: "Đang xóa người được chỉ định...",
success: {
title: "Thành công!",
message: "Đã xóa người được chỉ định thành công.",
},
error: {
title: "Lỗi!",
message: "Đã xảy ra lỗi khi xóa người được chỉ định. Vui lòng thử lại.",
},
},
set: {
loading: "Đang đặt người được chỉ định...",
success: {
title: "Thành công!",
message: "Đã đặt người được chỉ định thành công.",
message: "Người được chỉ định đã được đặt thành công.",
},
error: {
title: "Lỗi!",
@@ -168,22 +168,11 @@ export default {
description: "对于新的接收请求,默认负责人将通过通知收到提醒",
},
toasts: {
remove: {
loading: "正在移除负责人...",
success: {
title: "成功!",
message: "负责人已成功移除。",
},
error: {
title: "错误!",
message: "移除负责人时出现问题。请重试。",
},
},
set: {
loading: "正在设置负责人...",
success: {
title: "成功!",
message: "负责人已成功设置。",
message: "负责人设置成功。",
},
error: {
title: "错误!",
@@ -169,22 +169,11 @@ export default {
description: "對於新的接收請求,預設負責人將透過通知收到提醒",
},
toasts: {
remove: {
loading: "正在移除負責人...",
success: {
title: "成功!",
message: "負責人已成功移除。",
},
error: {
title: "錯誤!",
message: "移除負責人時出現問題。請重試。",
},
},
set: {
loading: "正在設定負責人...",
success: {
title: "成功!",
message: "負責人已成功設定。",
message: "負責人設定成功。",
},
error: {
title: "錯誤!",
@@ -67,11 +67,3 @@ export type TIntakePublishFormProps = {
export type TIntakeIssueExtended = {
additional_information: TIssuePropertySerializedEntry[];
};
export type TIntakeUser = {
id: string;
intake: string;
user: string | null;
type: "ASSIGNEE" | "SUBSCRIBER" | null;
project: string;
};