Simplify manager order editing UI

This commit is contained in:
Ruslan Bakiev
2026-04-06 12:22:33 +07:00
parent a2ab98823d
commit 5396354962
9 changed files with 126 additions and 368 deletions

View File

@@ -71,7 +71,7 @@ function currentUnitPrice(item: OrderItemView) {
return item.unitPrice ?? null;
}
return parseMoneyDraft(props.unitPriceDrafts?.[item.id], item.unitPrice);
return parseMoneyDraft(props.unitPriceDrafts?.[item.id]);
}
function currentLineTotal(item: OrderItemView) {

View File

@@ -47,11 +47,6 @@ export type AuthSession = {
user: User;
};
export type BlockOrderInput = {
orderId: Scalars['ID']['input'];
reason: Scalars['String']['input'];
};
export type BonusTransaction = {
__typename?: 'BonusTransaction';
amount: Scalars['Float']['output'];
@@ -279,18 +274,16 @@ export type Mutation = {
acceptInvitation: User;
addBonusTransaction: BonusTransaction;
addProductToCart: Cart;
blockOrder: Order;
clearCart: Cart;
clientReviewOrder: Order;
completeOrder: Order;
connectMessenger: MessengerConnection;
consumeLoginToken: AuthSession;
createInvitation: Invitation;
createMyDeliveryAddress: DeliveryAddress;
createReferral: ReferralLink;
deleteMyDeliveryAddress: Scalars['Boolean']['output'];
managerFinalizeOrder: Order;
managerSetOrderOffer: Order;
managerSetOrderStatus: Order;
registerSelf: RegistrationRequest;
removeCartItem: Cart;
requestLoginCode: AuthCodeRequestResult;
@@ -300,7 +293,6 @@ export type Mutation = {
sendTestMessengerMessage: MessengerDispatchResult;
setCartDeliveryAddress: Cart;
setMyDefaultDeliveryAddress: DeliveryAddress;
startOrderWork: Order;
submitCalculationOrder: Order;
submitReadyOrder: Order;
updateCartItemQuantity: Cart;
@@ -324,22 +316,12 @@ export type MutationAddProductToCartArgs = {
};
export type MutationBlockOrderArgs = {
input: BlockOrderInput;
};
export type MutationClientReviewOrderArgs = {
decision: Decision;
orderId: Scalars['ID']['input'];
};
export type MutationCompleteOrderArgs = {
orderId: Scalars['ID']['input'];
};
export type MutationConnectMessengerArgs = {
input: ConnectMessengerInput;
};
@@ -370,14 +352,14 @@ export type MutationDeleteMyDeliveryAddressArgs = {
};
export type MutationManagerFinalizeOrderArgs = {
decision: Decision;
orderId: Scalars['ID']['input'];
export type MutationManagerSetOrderOfferArgs = {
input: SetOrderOfferInput;
};
export type MutationManagerSetOrderOfferArgs = {
input: SetOrderOfferInput;
export type MutationManagerSetOrderStatusArgs = {
orderId: Scalars['ID']['input'];
status: OrderStatus;
};
@@ -428,11 +410,6 @@ export type MutationSetMyDefaultDeliveryAddressArgs = {
};
export type MutationStartOrderWorkArgs = {
orderId: Scalars['ID']['input'];
};
export type MutationSubmitCalculationOrderArgs = {
input: SubmitCalculationOrderInput;
};
@@ -501,7 +478,7 @@ export type OrderItem = {
export type OrderItemPriceInput = {
itemId: Scalars['ID']['input'];
unitPrice: Scalars['Float']['input'];
unitPrice?: InputMaybe<Scalars['Float']['input']>;
};
export enum OrderKind {
@@ -701,7 +678,7 @@ export type RewardWithdrawalRequest = {
};
export type SetOrderOfferInput = {
deliveryFee: Scalars['Float']['input'];
deliveryFee?: InputMaybe<Scalars['Float']['input']>;
deliveryTerms: Scalars['String']['input'];
itemPrices: Array<OrderItemPriceInput>;
orderId: Scalars['ID']['input'];
@@ -856,20 +833,6 @@ export type AddBonusTransactionMutationVariables = Exact<{
export type AddBonusTransactionMutation = { __typename?: 'Mutation', addBonusTransaction: { __typename?: 'BonusTransaction', id: string, userId: string, amount: number, reason: string, createdAt: any } };
export type BlockOrderMutationVariables = Exact<{
input: BlockOrderInput;
}>;
export type BlockOrderMutation = { __typename?: 'Mutation', blockOrder: { __typename?: 'Order', id: string, status: OrderStatus, blockReason?: string | null } };
export type CompleteOrderMutationVariables = Exact<{
orderId: Scalars['ID']['input'];
}>;
export type CompleteOrderMutation = { __typename?: 'Mutation', completeOrder: { __typename?: 'Order', id: string, status: OrderStatus } };
export type CreateInvitationMutationVariables = Exact<{
input: CreateInvitationInput;
}>;
@@ -896,14 +859,6 @@ export type ManagerBonusBalancesQueryVariables = Exact<{ [key: string]: never; }
export type ManagerBonusBalancesQuery = { __typename?: 'Query', managerBonusBalances: Array<{ __typename?: 'ManagerBonusBalance', userId: string, email: string, fullName: string, companyName?: string | null, balance: number, pendingWithdrawalAmount: number, transactionsCount: number }> };
export type ManagerFinalizeOrderMutationVariables = Exact<{
orderId: Scalars['ID']['input'];
decision: Decision;
}>;
export type ManagerFinalizeOrderMutation = { __typename?: 'Mutation', managerFinalizeOrder: { __typename?: 'Order', id: string, status: OrderStatus, managerApproved?: boolean | null } };
export type ManagerOrdersQueryVariables = Exact<{
status?: InputMaybe<OrderStatus>;
customerId?: InputMaybe<Scalars['ID']['input']>;
@@ -917,6 +872,14 @@ export type ManagerReferralLinksQueryVariables = Exact<{ [key: string]: never; }
export type ManagerReferralLinksQuery = { __typename?: 'Query', managerReferralLinks: Array<{ __typename?: 'ManagerReferralLink', id: string, referrerId: string, referrerName: string, referrerEmail: string, referrerCompanyName?: string | null, refereeId: string, refereeName: string, refereeEmail: string, refereeCompanyName?: string | null, createdById: string, bonusPercent: number, createdAt: any }> };
export type ManagerSetOrderStatusMutationVariables = Exact<{
orderId: Scalars['ID']['input'];
status: OrderStatus;
}>;
export type ManagerSetOrderStatusMutation = { __typename?: 'Mutation', managerSetOrderStatus: { __typename?: 'Order', id: string, status: OrderStatus } };
export type ManagerUsersDetailQueryVariables = Exact<{ [key: string]: never; }>;
@@ -967,13 +930,6 @@ export type ManagerSetOrderOfferMutationVariables = Exact<{
export type ManagerSetOrderOfferMutation = { __typename?: 'Mutation', managerSetOrderOffer: { __typename?: 'Order', id: string, code: string, status: OrderStatus, deliveryTerms?: string | null, deliveryFee?: number | null, totalPrice?: number | null, items: Array<{ __typename?: 'OrderItem', id: string, unitPrice?: number | null, lineTotal?: number | null }> } };
export type StartOrderWorkMutationVariables = Exact<{
orderId: Scalars['ID']['input'];
}>;
export type StartOrderWorkMutation = { __typename?: 'Mutation', startOrderWork: { __typename?: 'Order', id: string, status: OrderStatus } };
export type ManagerNotificationHistoryQueryVariables = Exact<{
userId: Scalars['ID']['input'];
channel: MessengerType;
@@ -1580,67 +1536,6 @@ export function useAddBonusTransactionMutation(options: VueApolloComposable.UseM
return VueApolloComposable.useMutation<AddBonusTransactionMutation, AddBonusTransactionMutationVariables>(AddBonusTransactionDocument, options);
}
export type AddBonusTransactionMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<AddBonusTransactionMutation, AddBonusTransactionMutationVariables>;
export const BlockOrderDocument = gql`
mutation BlockOrder($input: BlockOrderInput!) {
blockOrder(input: $input) {
id
status
blockReason
}
}
`;
/**
* __useBlockOrderMutation__
*
* To run a mutation, you first call `useBlockOrderMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useBlockOrderMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useBlockOrderMutation({
* variables: {
* input: // value for 'input'
* },
* });
*/
export function useBlockOrderMutation(options: VueApolloComposable.UseMutationOptions<BlockOrderMutation, BlockOrderMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<BlockOrderMutation, BlockOrderMutationVariables>> = {}) {
return VueApolloComposable.useMutation<BlockOrderMutation, BlockOrderMutationVariables>(BlockOrderDocument, options);
}
export type BlockOrderMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<BlockOrderMutation, BlockOrderMutationVariables>;
export const CompleteOrderDocument = gql`
mutation CompleteOrder($orderId: ID!) {
completeOrder(orderId: $orderId) {
id
status
}
}
`;
/**
* __useCompleteOrderMutation__
*
* To run a mutation, you first call `useCompleteOrderMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useCompleteOrderMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useCompleteOrderMutation({
* variables: {
* orderId: // value for 'orderId'
* },
* });
*/
export function useCompleteOrderMutation(options: VueApolloComposable.UseMutationOptions<CompleteOrderMutation, CompleteOrderMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<CompleteOrderMutation, CompleteOrderMutationVariables>> = {}) {
return VueApolloComposable.useMutation<CompleteOrderMutation, CompleteOrderMutationVariables>(CompleteOrderDocument, options);
}
export type CompleteOrderMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<CompleteOrderMutation, CompleteOrderMutationVariables>;
export const CreateInvitationDocument = gql`
mutation CreateInvitation($input: CreateInvitationInput!) {
createInvitation(input: $input) {
@@ -1810,38 +1705,6 @@ export function useManagerBonusBalancesLazyQuery(options: VueApolloComposable.Us
return VueApolloComposable.useLazyQuery<ManagerBonusBalancesQuery, ManagerBonusBalancesQueryVariables>(ManagerBonusBalancesDocument, {}, options);
}
export type ManagerBonusBalancesQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<ManagerBonusBalancesQuery, ManagerBonusBalancesQueryVariables>;
export const ManagerFinalizeOrderDocument = gql`
mutation ManagerFinalizeOrder($orderId: ID!, $decision: Decision!) {
managerFinalizeOrder(orderId: $orderId, decision: $decision) {
id
status
managerApproved
}
}
`;
/**
* __useManagerFinalizeOrderMutation__
*
* To run a mutation, you first call `useManagerFinalizeOrderMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useManagerFinalizeOrderMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useManagerFinalizeOrderMutation({
* variables: {
* orderId: // value for 'orderId'
* decision: // value for 'decision'
* },
* });
*/
export function useManagerFinalizeOrderMutation(options: VueApolloComposable.UseMutationOptions<ManagerFinalizeOrderMutation, ManagerFinalizeOrderMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ManagerFinalizeOrderMutation, ManagerFinalizeOrderMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ManagerFinalizeOrderMutation, ManagerFinalizeOrderMutationVariables>(ManagerFinalizeOrderDocument, options);
}
export type ManagerFinalizeOrderMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ManagerFinalizeOrderMutation, ManagerFinalizeOrderMutationVariables>;
export const ManagerOrdersDocument = gql`
query ManagerOrders($status: OrderStatus, $customerId: ID) {
managerOrders(status: $status, customerId: $customerId) {
@@ -1927,6 +1790,37 @@ export function useManagerReferralLinksLazyQuery(options: VueApolloComposable.Us
return VueApolloComposable.useLazyQuery<ManagerReferralLinksQuery, ManagerReferralLinksQueryVariables>(ManagerReferralLinksDocument, {}, options);
}
export type ManagerReferralLinksQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<ManagerReferralLinksQuery, ManagerReferralLinksQueryVariables>;
export const ManagerSetOrderStatusDocument = gql`
mutation ManagerSetOrderStatus($orderId: ID!, $status: OrderStatus!) {
managerSetOrderStatus(orderId: $orderId, status: $status) {
id
status
}
}
`;
/**
* __useManagerSetOrderStatusMutation__
*
* To run a mutation, you first call `useManagerSetOrderStatusMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useManagerSetOrderStatusMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useManagerSetOrderStatusMutation({
* variables: {
* orderId: // value for 'orderId'
* status: // value for 'status'
* },
* });
*/
export function useManagerSetOrderStatusMutation(options: VueApolloComposable.UseMutationOptions<ManagerSetOrderStatusMutation, ManagerSetOrderStatusMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ManagerSetOrderStatusMutation, ManagerSetOrderStatusMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ManagerSetOrderStatusMutation, ManagerSetOrderStatusMutationVariables>(ManagerSetOrderStatusDocument, options);
}
export type ManagerSetOrderStatusMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ManagerSetOrderStatusMutation, ManagerSetOrderStatusMutationVariables>;
export const ManagerUsersDetailDocument = gql`
query ManagerUsersDetail {
managerUsers {
@@ -2232,36 +2126,6 @@ export function useManagerSetOrderOfferMutation(options: VueApolloComposable.Use
return VueApolloComposable.useMutation<ManagerSetOrderOfferMutation, ManagerSetOrderOfferMutationVariables>(ManagerSetOrderOfferDocument, options);
}
export type ManagerSetOrderOfferMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ManagerSetOrderOfferMutation, ManagerSetOrderOfferMutationVariables>;
export const StartOrderWorkDocument = gql`
mutation StartOrderWork($orderId: ID!) {
startOrderWork(orderId: $orderId) {
id
status
}
}
`;
/**
* __useStartOrderWorkMutation__
*
* To run a mutation, you first call `useStartOrderWorkMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useStartOrderWorkMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useStartOrderWorkMutation({
* variables: {
* orderId: // value for 'orderId'
* },
* });
*/
export function useStartOrderWorkMutation(options: VueApolloComposable.UseMutationOptions<StartOrderWorkMutation, StartOrderWorkMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<StartOrderWorkMutation, StartOrderWorkMutationVariables>> = {}) {
return VueApolloComposable.useMutation<StartOrderWorkMutation, StartOrderWorkMutationVariables>(StartOrderWorkDocument, options);
}
export type StartOrderWorkMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<StartOrderWorkMutation, StartOrderWorkMutationVariables>;
export const ManagerNotificationHistoryDocument = gql`
query ManagerNotificationHistory($userId: ID!, $channel: MessengerType!, $limit: Int) {
managerNotificationHistory(userId: $userId, channel: $channel, limit: $limit) {

View File

@@ -1,12 +1,10 @@
<script setup lang="ts">
import { useMutation, useQuery } from '@vue/apollo-composable';
import {
CompleteOrderDocument,
Decision,
ManagerFinalizeOrderDocument,
ManagerSetOrderOfferDocument,
ManagerSetOrderStatusDocument,
OrderStatus,
OrderDetailDocument,
StartOrderWorkDocument,
type OrderDetailQuery,
} from '~/composables/graphql/generated';
import {
@@ -24,9 +22,8 @@ const route = useRoute();
const orderId = computed(() => String(route.params.id || ''));
type ManagerOrderItem = NonNullable<OrderDetailQuery['order']>;
type StatusAction = 'APPROVE' | 'REJECT' | 'START' | 'COMPLETE';
type StatusActionOption = {
value: StatusAction;
type StatusOption = {
value: OrderStatus;
label: string;
};
@@ -34,10 +31,8 @@ const orderQuery = useQuery(OrderDetailDocument, () => ({
id: orderId.value,
}));
const completeOrderMutation = useMutation(CompleteOrderDocument);
const finalizeOrderMutation = useMutation(ManagerFinalizeOrderDocument);
const startWorkMutation = useMutation(StartOrderWorkDocument);
const setOfferMutation = useMutation(ManagerSetOrderOfferDocument);
const setOrderStatusMutation = useMutation(ManagerSetOrderStatusDocument);
const itemPriceDrafts = reactive<Record<string, string>>({});
const deliveryTermsDraft = ref('');
@@ -46,7 +41,7 @@ const editingPriceItemId = ref<string | null>(null);
const editingDeliveryTerms = ref(false);
const editingDeliveryFee = ref(false);
const editingStatus = ref(false);
const statusActionDraft = ref<StatusAction | ''>('');
const statusDraft = ref<OrderStatus | ''>('');
const autosavePending = ref(false);
const statusMutationPending = ref(false);
let autosaveTimer: ReturnType<typeof setTimeout> | null = null;
@@ -64,7 +59,7 @@ watch(
editingDeliveryTerms.value = false;
editingDeliveryFee.value = false;
editingStatus.value = false;
statusActionDraft.value = '';
statusDraft.value = '';
for (const key of Object.keys(itemPriceDrafts)) {
delete itemPriceDrafts[key];
@@ -101,63 +96,22 @@ function parseMoneyDraft(value: string) {
}
const draftDeliveryTerms = computed(() => deliveryTermsDraft.value.trim() || currentOrder.value?.deliveryTerms || null);
const draftDeliveryFee = computed(() => parseMoneyDraft(deliveryFeeDraft.value) ?? currentOrder.value?.deliveryFee ?? null);
const canEditOffer = computed(() => (
currentOrder.value != null
&& ['NEW', 'MANAGER_PROCESSING', 'WAITING_DOUBLE_CONFIRM', 'CONFIRMED'].includes(currentOrder.value.status)
));
const draftDeliveryFee = computed(() => parseMoneyDraft(deliveryFeeDraft.value));
const canEditOffer = computed(() => currentOrder.value != null);
const activePriceItemIds = computed(() => (
editingPriceItemId.value ? [editingPriceItemId.value] : []
));
const managerStatusSummary = computed(() => {
if (!currentOrder.value) {
return '';
}
if (['NEW', 'MANAGER_PROCESSING'].includes(currentOrder.value.status)) {
return 'Заполните цены по позициям и логистике. После этого заказ можно перевести в предложение.';
}
if (['WAITING_DOUBLE_CONFIRM', 'CONFIRMED'].includes(currentOrder.value.status)) {
return 'Предложение уже собрано. Следующий шаг для менеджера: перевести заказ в работу или остановить его.';
}
if (currentOrder.value.status === 'IN_PROGRESS') {
return 'Заказ уже в работе. После исполнения его можно завершить.';
}
if (currentOrder.value.status === 'COMPLETED') {
return 'Заказ завершён и больше не редактируется.';
}
return 'Заказ остановлен. Если нужно продолжить работу, сначала поправьте условия и затем снова переведите его в предложение.';
});
const statusActions = computed<StatusActionOption[]>(() => {
if (!currentOrder.value) {
return [];
}
const options: StatusActionOption[] = [];
if (offerReady.value && ['NEW', 'MANAGER_PROCESSING', 'MANAGER_BLOCKED', 'CLIENT_REJECTED', 'MANAGER_REJECTED'].includes(currentOrder.value.status)) {
options.push({ value: 'APPROVE', label: 'Предложение' });
}
if (['NEW', 'MANAGER_PROCESSING', 'WAITING_DOUBLE_CONFIRM', 'CONFIRMED', 'MANAGER_BLOCKED'].includes(currentOrder.value.status)) {
options.push({ value: 'REJECT', label: 'Отклонен' });
}
if (['WAITING_DOUBLE_CONFIRM', 'CONFIRMED'].includes(currentOrder.value.status)) {
options.push({ value: 'START', label: 'В работе' });
}
if (currentOrder.value.status === 'IN_PROGRESS') {
options.push({ value: 'COMPLETE', label: 'Завершен' });
}
return options;
});
const canEditStatus = computed(() => statusActions.value.length > 0);
const statusOptions: StatusOption[] = [
{ value: OrderStatus.New, label: 'Заявка' },
{ value: OrderStatus.ManagerProcessing, label: 'В обработке' },
{ value: OrderStatus.WaitingDoubleConfirm, label: 'Предложение' },
{ value: OrderStatus.Confirmed, label: 'Подтвержден' },
{ value: OrderStatus.InProgress, label: 'В работе' },
{ value: OrderStatus.Completed, label: 'Завершен' },
{ value: OrderStatus.ManagerBlocked, label: 'Пауза' },
{ value: OrderStatus.ManagerRejected, label: 'Отклонен менеджером' },
{ value: OrderStatus.ClientRejected, label: 'Отклонен клиентом' },
];
const offerSignature = computed(() => {
if (!currentOrder.value) {
@@ -224,7 +178,7 @@ async function focusElement(selector: string) {
}
async function saveOffer() {
if (!currentOrder.value || !offerReady.value || !canEditOffer.value) {
if (!currentOrder.value || !canEditOffer.value) {
return;
}
@@ -235,10 +189,10 @@ async function saveOffer() {
orderId: currentOrder.value.id,
itemPrices: currentOrder.value.items.map((item) => ({
itemId: item.id,
unitPrice: parseMoneyDraft(itemPriceDrafts[item.id] ?? '') ?? 0,
unitPrice: parseMoneyDraft(itemPriceDrafts[item.id] ?? ''),
})),
deliveryTerms: deliveryTermsDraft.value.trim(),
deliveryFee: parseMoneyDraft(deliveryFeeDraft.value) ?? 0,
deliveryFee: parseMoneyDraft(deliveryFeeDraft.value),
},
});
await refetchOrder();
@@ -290,24 +244,29 @@ function closeDeliveryFeeEditor() {
}
async function openStatusEditor() {
if (!canEditStatus.value || statusMutationPending.value) {
if (statusMutationPending.value) {
return;
}
editingStatus.value = true;
statusActionDraft.value = '';
statusDraft.value = currentOrder.value?.status ?? '';
await focusElement('[data-manager-status-select]');
}
function closeStatusEditor() {
if (!statusMutationPending.value) {
editingStatus.value = false;
statusActionDraft.value = '';
statusDraft.value = '';
}
}
async function applyStatusAction() {
if (!currentOrder.value || !statusActionDraft.value) {
async function applyStatusChange() {
if (!currentOrder.value || !statusDraft.value) {
closeStatusEditor();
return;
}
if (statusDraft.value === currentOrder.value.status) {
closeStatusEditor();
return;
}
@@ -315,25 +274,10 @@ async function applyStatusAction() {
statusMutationPending.value = true;
try {
if (statusActionDraft.value === 'APPROVE') {
await finalizeOrderMutation.mutate({
await setOrderStatusMutation.mutate({
orderId: currentOrder.value.id,
decision: Decision.Approve,
status: statusDraft.value,
});
}
else if (statusActionDraft.value === 'REJECT') {
await finalizeOrderMutation.mutate({
orderId: currentOrder.value.id,
decision: Decision.Reject,
});
}
else if (statusActionDraft.value === 'START') {
await startWorkMutation.mutate({ orderId: currentOrder.value.id });
}
else if (statusActionDraft.value === 'COMPLETE') {
await completeOrderMutation.mutate({ orderId: currentOrder.value.id });
}
await refetchOrder();
}
finally {
@@ -343,14 +287,14 @@ async function applyStatusAction() {
}
watch(
[offerReady, offerSignature, publishedSignature],
([ready, nextSignature, currentSignature]) => {
[offerSignature, publishedSignature],
([nextSignature, currentSignature]) => {
if (autosaveTimer) {
clearTimeout(autosaveTimer);
autosaveTimer = null;
}
if (!ready || !canEditOffer.value || !nextSignature || nextSignature === currentSignature || autosavePending.value) {
if (!canEditOffer.value || !nextSignature || nextSignature === currentSignature || autosavePending.value) {
return;
}
@@ -381,22 +325,19 @@ watch(
<div class="space-y-4">
<div class="surface-card rounded-3xl p-5">
<div class="flex flex-wrap items-start justify-between gap-4">
<div class="space-y-2">
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-[#6a8a76]">Статус заказа</p>
<label v-if="editingStatus && canEditStatus" class="block">
<label v-if="editingStatus" class="mt-3 block">
<select
v-model="statusActionDraft"
v-model="statusDraft"
data-manager-status-select
class="w-full min-w-[220px] rounded-2xl bg-[#f3f5f4] px-4 py-3 text-sm font-semibold text-[#123824] outline-none transition focus:shadow-[0_0_0_3px_rgba(19,153,87,0.12)]"
:disabled="statusMutationPending"
@change="void applyStatusAction()"
@change="void applyStatusChange()"
@blur="closeStatusEditor"
>
<option value="" disabled>Выберите статус</option>
<option
v-for="option in statusActions"
v-for="option in statusOptions"
:key="option.value"
:value="option.value"
>
@@ -408,30 +349,13 @@ watch(
<button
v-else
type="button"
class="rounded-2xl px-0 py-0 text-left"
:class="canEditStatus ? 'cursor-pointer' : 'cursor-default'"
class="mt-3 rounded-2xl px-0 py-0 text-left"
@dblclick="void openStatusEditor()"
>
<OrdersOrderStatusBadge :status="currentOrder.status" />
</button>
</div>
<p class="text-sm text-[#5c7b69]">
{{
statusMutationPending
? 'Обновляем статус...'
: canEditStatus
? 'Двойной клик по статусу, чтобы изменить.'
: 'Статус уже финальный.'
}}
</p>
</div>
<p class="mt-4 max-w-2xl text-sm leading-6 text-[#355947]">
{{ managerStatusSummary }}
</p>
</div>
<div>
<h2 class="text-xl font-bold text-[#123824]">Состав заказа</h2>
<OrdersOrderItemsTable
@@ -515,16 +439,14 @@ watch(
<p class="text-sm text-[#5c7b69]">
{{
offerTotal == null
? 'Итог по заказу появится после заполнения всех цен.'
? currentOrder.totalPrice == null
? 'Итог пока не задан.'
: `Текущий итог: ${formatPrice(currentOrder.totalPrice)}`
: autosavePending
? `Сохраняем: ${formatPrice(offerTotal)}`
: `Текущий итог: ${formatPrice(offerTotal)}`
}}
</p>
<p v-if="canEditOffer" class="text-xs font-semibold uppercase tracking-[0.14em] text-[#6a8a76]">
Двойной клик по цене или условиям, чтобы изменить.
</p>
</div>
</div>
</div>

View File

@@ -1,7 +0,0 @@
mutation BlockOrder($input: BlockOrderInput!) {
blockOrder(input: $input) {
id
status
blockReason
}
}

View File

@@ -1,6 +0,0 @@
mutation CompleteOrder($orderId: ID!) {
completeOrder(orderId: $orderId) {
id
status
}
}

View File

@@ -1,7 +0,0 @@
mutation ManagerFinalizeOrder($orderId: ID!, $decision: Decision!) {
managerFinalizeOrder(orderId: $orderId, decision: $decision) {
id
status
managerApproved
}
}

View File

@@ -0,0 +1,6 @@
mutation ManagerSetOrderStatus($orderId: ID!, $status: OrderStatus!) {
managerSetOrderStatus(orderId: $orderId, status: $status) {
id
status
}
}

View File

@@ -1,6 +0,0 @@
mutation StartOrderWork($orderId: ID!) {
startOrderWork(orderId: $orderId) {
id
status
}
}

View File

@@ -467,17 +467,12 @@ input SetOrderOfferInput {
orderId: ID!
itemPrices: [OrderItemPriceInput!]!
deliveryTerms: String!
deliveryFee: Float!
deliveryFee: Float
}
input OrderItemPriceInput {
itemId: ID!
unitPrice: Float!
}
input BlockOrderInput {
orderId: ID!
reason: String!
unitPrice: Float
}
input CreateReferralInput {
@@ -526,11 +521,8 @@ type Mutation {
submitReadyOrder(input: SubmitReadyOrderInput!): Order!
submitCalculationOrder(input: SubmitCalculationOrderInput!): Order!
managerSetOrderOffer(input: SetOrderOfferInput!): Order!
managerSetOrderStatus(orderId: ID!, status: OrderStatus!): Order!
clientReviewOrder(orderId: ID!, decision: Decision!): Order!
managerFinalizeOrder(orderId: ID!, decision: Decision!): Order!
blockOrder(input: BlockOrderInput!): Order!
startOrderWork(orderId: ID!): Order!
completeOrder(orderId: ID!): Order!
createReferral(input: CreateReferralInput!): ReferralLink!
addBonusTransaction(input: AddBonusTransactionInput!): BonusTransaction!