[WEB-5568] feat: Allow Multiple Assignees for Intake Responsibility (#4970)
This commit is contained in:
@@ -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. Vă rugăm să încercați 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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user