diff --git a/app/composables/graphql/generated.ts b/app/composables/graphql/generated.ts index 849c262..3a6192f 100644 --- a/app/composables/graphql/generated.ts +++ b/app/composables/graphql/generated.ts @@ -102,6 +102,13 @@ export type CreateInvitationInput = { expiresInDays?: InputMaybe; }; +export type CreateMyDeliveryAddressInput = { + address: Scalars['String']['input']; + fiasId?: InputMaybe; + label?: InputMaybe; + unrestrictedValue?: InputMaybe; +}; + export type CreateReferralInput = { refereeUserId: Scalars['ID']['input']; }; @@ -111,6 +118,19 @@ export enum Decision { Reject = 'REJECT' } +export type DeliveryAddress = { + __typename?: 'DeliveryAddress'; + address: Scalars['String']['output']; + createdAt: Scalars['DateTime']['output']; + fiasId?: Maybe; + id: Scalars['ID']['output']; + isDefault: Scalars['Boolean']['output']; + label?: Maybe; + unrestrictedValue?: Maybe; + updatedAt: Scalars['DateTime']['output']; + userId: Scalars['ID']['output']; +}; + export type Invitation = { __typename?: 'Invitation'; acceptedAt?: Maybe; @@ -163,7 +183,9 @@ export type Mutation = { connectMessenger: MessengerConnection; consumeLoginToken: AuthSession; createInvitation: Invitation; + createMyDeliveryAddress: DeliveryAddress; createReferral: ReferralLink; + deleteMyDeliveryAddress: Scalars['Boolean']['output']; managerFinalizeOrder: Order; managerSetOrderOffer: Order; registerSelf: RegistrationRequest; @@ -172,6 +194,7 @@ export type Mutation = { reviewRegistrationRequest: RegistrationRequest; reviewRewardWithdrawal: RewardWithdrawalRequest; sendTestMessengerMessage: MessengerDispatchResult; + setMyDefaultDeliveryAddress: DeliveryAddress; startOrderWork: Order; submitCalculationOrder: Order; submitReadyOrder: Order; @@ -221,11 +244,21 @@ export type MutationCreateInvitationArgs = { }; +export type MutationCreateMyDeliveryAddressArgs = { + input: CreateMyDeliveryAddressInput; +}; + + export type MutationCreateReferralArgs = { input: CreateReferralInput; }; +export type MutationDeleteMyDeliveryAddressArgs = { + addressId: Scalars['ID']['input']; +}; + + export type MutationManagerFinalizeOrderArgs = { decision: Decision; orderId: Scalars['ID']['input']; @@ -269,6 +302,11 @@ export type MutationSendTestMessengerMessageArgs = { }; +export type MutationSetMyDefaultDeliveryAddressArgs = { + addressId: Scalars['ID']['input']; +}; + + export type MutationStartOrderWorkArgs = { orderId: Scalars['ID']['input']; }; @@ -311,6 +349,7 @@ export type Order = { code: Scalars['String']['output']; createdAt: Scalars['DateTime']['output']; customerId: Scalars['ID']['output']; + deliveryAddress?: Maybe; deliveryFee?: Maybe; deliveryTerms?: Maybe; history: Array; @@ -384,6 +423,7 @@ export type Query = { me?: Maybe; myCounterpartyProfile?: Maybe; myCurrentOrders: Array; + myDeliveryAddresses: Array; myMessengerConnections: Array; myNotificationHistory: Array; myOrders: Array; @@ -504,12 +544,14 @@ export type SetOrderOfferInput = { }; export type SubmitCalculationOrderInput = { + deliveryAddressId?: InputMaybe; parameters: Scalars['JSON']['input']; productName: Scalars['String']['input']; quantity: Scalars['Float']['input']; }; export type SubmitReadyOrderInput = { + deliveryAddressId?: InputMaybe; items: Array; }; @@ -637,7 +679,7 @@ export type MyCurrentOrdersQuery = { __typename?: 'Query', myCurrentOrders: Arra export type MyOrdersQueryVariables = Exact<{ [key: string]: never; }>; -export type MyOrdersQuery = { __typename?: 'Query', myOrders: Array<{ __typename?: 'Order', id: string, code: string, kind: OrderKind, status: OrderStatus, totalPrice?: number | null, deliveryTerms?: string | null, createdAt: any, items: Array<{ __typename?: 'OrderItem', id: string, productName: string, quantity: number }> }> }; +export type MyOrdersQuery = { __typename?: 'Query', myOrders: Array<{ __typename?: 'Order', id: string, code: string, kind: OrderKind, status: OrderStatus, deliveryAddress?: string | null, totalPrice?: number | null, deliveryTerms?: string | null, createdAt: any, items: Array<{ __typename?: 'OrderItem', id: string, productName: string, quantity: number }> }> }; export type SubmitCalculationOrderMutationVariables = Exact<{ input: SubmitCalculationOrderInput; @@ -660,11 +702,37 @@ export type ConnectMessengerMutationVariables = Exact<{ export type ConnectMessengerMutation = { __typename?: 'Mutation', connectMessenger: { __typename?: 'MessengerConnection', id: string, type: MessengerType, channelId: string, isActive: boolean } }; +export type CreateMyDeliveryAddressMutationVariables = Exact<{ + input: CreateMyDeliveryAddressInput; +}>; + + +export type CreateMyDeliveryAddressMutation = { __typename?: 'Mutation', createMyDeliveryAddress: { __typename?: 'DeliveryAddress', id: string, label?: string | null, address: string, unrestrictedValue?: string | null, fiasId?: string | null, isDefault: boolean, updatedAt: any } }; + +export type DeleteMyDeliveryAddressMutationVariables = Exact<{ + addressId: Scalars['ID']['input']; +}>; + + +export type DeleteMyDeliveryAddressMutation = { __typename?: 'Mutation', deleteMyDeliveryAddress: boolean }; + export type MyCounterpartyProfileQueryVariables = Exact<{ [key: string]: never; }>; export type MyCounterpartyProfileQuery = { __typename?: 'Query', myCounterpartyProfile?: { __typename?: 'CounterpartyProfile', id: string, companyName: string, companyFullName: string, inn: string, kpp?: string | null, ogrn?: string | null, legalAddress: string, bankName: string, bik: string, correspondentAccount: string, checkingAccount: string, signerFullName: string, signerPosition: string, signerBasis: string, isComplete: boolean, updatedAt: any } | null }; +export type MyDeliveryAddressesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type MyDeliveryAddressesQuery = { __typename?: 'Query', myDeliveryAddresses: Array<{ __typename?: 'DeliveryAddress', id: string, label?: string | null, address: string, unrestrictedValue?: string | null, fiasId?: string | null, isDefault: boolean, updatedAt: any }> }; + +export type SetMyDefaultDeliveryAddressMutationVariables = Exact<{ + addressId: Scalars['ID']['input']; +}>; + + +export type SetMyDefaultDeliveryAddressMutation = { __typename?: 'Mutation', setMyDefaultDeliveryAddress: { __typename?: 'DeliveryAddress', id: string, label?: string | null, address: string, unrestrictedValue?: string | null, fiasId?: string | null, isDefault: boolean, updatedAt: any } }; + export type UpsertMyCounterpartyProfileMutationVariables = Exact<{ input: UpsertMyCounterpartyProfileInput; }>; @@ -1061,6 +1129,7 @@ export const MyOrdersDocument = gql` code kind status + deliveryAddress totalPrice deliveryTerms createdAt @@ -1188,6 +1257,68 @@ export function useConnectMessengerMutation(options: VueApolloComposable.UseMuta return VueApolloComposable.useMutation(ConnectMessengerDocument, options); } export type ConnectMessengerMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn; +export const CreateMyDeliveryAddressDocument = gql` + mutation CreateMyDeliveryAddress($input: CreateMyDeliveryAddressInput!) { + createMyDeliveryAddress(input: $input) { + id + label + address + unrestrictedValue + fiasId + isDefault + updatedAt + } +} + `; + +/** + * __useCreateMyDeliveryAddressMutation__ + * + * To run a mutation, you first call `useCreateMyDeliveryAddressMutation` within a Vue component and pass it any options that fit your needs. + * When your component renders, `useCreateMyDeliveryAddressMutation` 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 } = useCreateMyDeliveryAddressMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useCreateMyDeliveryAddressMutation(options: VueApolloComposable.UseMutationOptions | ReactiveFunction> = {}) { + return VueApolloComposable.useMutation(CreateMyDeliveryAddressDocument, options); +} +export type CreateMyDeliveryAddressMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn; +export const DeleteMyDeliveryAddressDocument = gql` + mutation DeleteMyDeliveryAddress($addressId: ID!) { + deleteMyDeliveryAddress(addressId: $addressId) +} + `; + +/** + * __useDeleteMyDeliveryAddressMutation__ + * + * To run a mutation, you first call `useDeleteMyDeliveryAddressMutation` within a Vue component and pass it any options that fit your needs. + * When your component renders, `useDeleteMyDeliveryAddressMutation` 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 } = useDeleteMyDeliveryAddressMutation({ + * variables: { + * addressId: // value for 'addressId' + * }, + * }); + */ +export function useDeleteMyDeliveryAddressMutation(options: VueApolloComposable.UseMutationOptions | ReactiveFunction> = {}) { + return VueApolloComposable.useMutation(DeleteMyDeliveryAddressDocument, options); +} +export type DeleteMyDeliveryAddressMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn; export const MyCounterpartyProfileDocument = gql` query MyCounterpartyProfile { myCounterpartyProfile { @@ -1230,6 +1361,74 @@ export function useMyCounterpartyProfileLazyQuery(options: VueApolloComposable.U return VueApolloComposable.useLazyQuery(MyCounterpartyProfileDocument, {}, options); } export type MyCounterpartyProfileQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn; +export const MyDeliveryAddressesDocument = gql` + query MyDeliveryAddresses { + myDeliveryAddresses { + id + label + address + unrestrictedValue + fiasId + isDefault + updatedAt + } +} + `; + +/** + * __useMyDeliveryAddressesQuery__ + * + * To run a query within a Vue component, call `useMyDeliveryAddressesQuery` and pass it any options that fit your needs. + * When your component renders, `useMyDeliveryAddressesQuery` returns an object from Apollo Client that contains result, loading and error properties + * you can use to render your UI. + * + * @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options; + * + * @example + * const { result, loading, error } = useMyDeliveryAddressesQuery(); + */ +export function useMyDeliveryAddressesQuery(options: VueApolloComposable.UseQueryOptions | VueCompositionApi.Ref> | ReactiveFunction> = {}) { + return VueApolloComposable.useQuery(MyDeliveryAddressesDocument, {}, options); +} +export function useMyDeliveryAddressesLazyQuery(options: VueApolloComposable.UseQueryOptions | VueCompositionApi.Ref> | ReactiveFunction> = {}) { + return VueApolloComposable.useLazyQuery(MyDeliveryAddressesDocument, {}, options); +} +export type MyDeliveryAddressesQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn; +export const SetMyDefaultDeliveryAddressDocument = gql` + mutation SetMyDefaultDeliveryAddress($addressId: ID!) { + setMyDefaultDeliveryAddress(addressId: $addressId) { + id + label + address + unrestrictedValue + fiasId + isDefault + updatedAt + } +} + `; + +/** + * __useSetMyDefaultDeliveryAddressMutation__ + * + * To run a mutation, you first call `useSetMyDefaultDeliveryAddressMutation` within a Vue component and pass it any options that fit your needs. + * When your component renders, `useSetMyDefaultDeliveryAddressMutation` 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 } = useSetMyDefaultDeliveryAddressMutation({ + * variables: { + * addressId: // value for 'addressId' + * }, + * }); + */ +export function useSetMyDefaultDeliveryAddressMutation(options: VueApolloComposable.UseMutationOptions | ReactiveFunction> = {}) { + return VueApolloComposable.useMutation(SetMyDefaultDeliveryAddressDocument, options); +} +export type SetMyDefaultDeliveryAddressMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn; export const UpsertMyCounterpartyProfileDocument = gql` mutation UpsertMyCounterpartyProfile($input: UpsertMyCounterpartyProfileInput!) { upsertMyCounterpartyProfile(input: $input) { diff --git a/app/pages/cart.vue b/app/pages/cart.vue index 831112d..736d18a 100644 --- a/app/pages/cart.vue +++ b/app/pages/cart.vue @@ -1,11 +1,19 @@ @@ -294,8 +494,48 @@ onBeforeUnmount(() => {

Профиль

-
-
+
+
+

Карточка контрагента

+

+ {{ profileIsComplete ? 'Заполнена' : 'Требует внимания' }} +

+

+ {{ + profileIsComplete + ? 'Можно оформлять заказы и отправлять заявки менеджеру.' + : 'Заполните реквизиты, чтобы открыть оформление заявок.' + }} +

+ +
+ +
+

Уведомления

+

{{ connectedMessengerCount }}/2 каналов подключено

+

{{ notificationsSummary }}

+ +
+ +
+

Адреса доставки

+

{{ deliveryAddresses.length }}

+

+ {{ + defaultDeliveryAddress + ? `Основной: ${defaultDeliveryAddress.label || defaultDeliveryAddress.address}` + : 'Добавьте хотя бы один адрес для оформления заявок.' + }} +

+ +
+
+ +
+

Карточка контрагента

@@ -305,7 +545,7 @@ onBeforeUnmount(() => {

-

1. Контрагент (Dadata)

+

1. Контрагент (DaData)

@@ -377,7 +617,7 @@ onBeforeUnmount(() => {
-

2. Банк (Dadata)

+

2. Банк (DaData)

@@ -473,42 +713,136 @@ onBeforeUnmount(() => {
- +
diff --git a/graphql/operations/orders/my-orders.graphql b/graphql/operations/orders/my-orders.graphql index 5895368..600e6ee 100644 --- a/graphql/operations/orders/my-orders.graphql +++ b/graphql/operations/orders/my-orders.graphql @@ -4,6 +4,7 @@ query MyOrders { code kind status + deliveryAddress totalPrice deliveryTerms createdAt diff --git a/graphql/operations/profile/create-my-delivery-address.graphql b/graphql/operations/profile/create-my-delivery-address.graphql new file mode 100644 index 0000000..2b8e731 --- /dev/null +++ b/graphql/operations/profile/create-my-delivery-address.graphql @@ -0,0 +1,11 @@ +mutation CreateMyDeliveryAddress($input: CreateMyDeliveryAddressInput!) { + createMyDeliveryAddress(input: $input) { + id + label + address + unrestrictedValue + fiasId + isDefault + updatedAt + } +} diff --git a/graphql/operations/profile/delete-my-delivery-address.graphql b/graphql/operations/profile/delete-my-delivery-address.graphql new file mode 100644 index 0000000..85eb70b --- /dev/null +++ b/graphql/operations/profile/delete-my-delivery-address.graphql @@ -0,0 +1,3 @@ +mutation DeleteMyDeliveryAddress($addressId: ID!) { + deleteMyDeliveryAddress(addressId: $addressId) +} diff --git a/graphql/operations/profile/my-delivery-addresses.graphql b/graphql/operations/profile/my-delivery-addresses.graphql new file mode 100644 index 0000000..a62496a --- /dev/null +++ b/graphql/operations/profile/my-delivery-addresses.graphql @@ -0,0 +1,11 @@ +query MyDeliveryAddresses { + myDeliveryAddresses { + id + label + address + unrestrictedValue + fiasId + isDefault + updatedAt + } +} diff --git a/graphql/operations/profile/set-my-default-delivery-address.graphql b/graphql/operations/profile/set-my-default-delivery-address.graphql new file mode 100644 index 0000000..a7bb859 --- /dev/null +++ b/graphql/operations/profile/set-my-default-delivery-address.graphql @@ -0,0 +1,11 @@ +mutation SetMyDefaultDeliveryAddress($addressId: ID!) { + setMyDefaultDeliveryAddress(addressId: $addressId) { + id + label + address + unrestrictedValue + fiasId + isDefault + updatedAt + } +} diff --git a/graphql/schema.graphql b/graphql/schema.graphql index 2511af2..6339163 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -86,6 +86,18 @@ type CounterpartyProfile { updatedAt: DateTime! } +type DeliveryAddress { + id: ID! + userId: ID! + label: String + address: String! + unrestrictedValue: String + fiasId: String + isDefault: Boolean! + createdAt: DateTime! + updatedAt: DateTime! +} + type AuthCodeRequestResult { challengeToken: String! channel: LoginChannel! @@ -191,6 +203,7 @@ type Order { kind: OrderKind! status: OrderStatus! customerId: ID! + deliveryAddress: String managerId: ID clientApproved: Boolean managerApproved: Boolean @@ -244,6 +257,7 @@ type Query { healthcheck: String! me: User myCounterpartyProfile: CounterpartyProfile + myDeliveryAddresses: [DeliveryAddress!]! myMessengerConnections: [MessengerConnection!]! myNotificationHistory(channel: MessengerType!, limit: Int = 50): [NotificationHistoryItem!]! managerNotificationHistory(userId: ID!, channel: MessengerType!, limit: Int = 50): [NotificationHistoryItem!]! @@ -310,6 +324,13 @@ input UpsertMyCounterpartyProfileInput { signerBasis: String! } +input CreateMyDeliveryAddressInput { + label: String + address: String! + unrestrictedValue: String + fiasId: String +} + input ReadyOrderItemInput { productId: ID! quantity: Float! @@ -317,12 +338,14 @@ input ReadyOrderItemInput { input SubmitReadyOrderInput { items: [ReadyOrderItemInput!]! + deliveryAddressId: ID } input SubmitCalculationOrderInput { productName: String! quantity: Float! parameters: JSON! + deliveryAddressId: ID } input SetOrderOfferInput { @@ -368,6 +391,9 @@ type Mutation { acceptInvitation(input: AcceptInvitationInput!): User! connectMessenger(input: ConnectMessengerInput!): MessengerConnection! upsertMyCounterpartyProfile(input: UpsertMyCounterpartyProfileInput!): CounterpartyProfile! + createMyDeliveryAddress(input: CreateMyDeliveryAddressInput!): DeliveryAddress! + setMyDefaultDeliveryAddress(addressId: ID!): DeliveryAddress! + deleteMyDeliveryAddress(addressId: ID!): Boolean! sendTestMessengerMessage(type: MessengerType!, channelId: String, message: String): MessengerDispatchResult! submitReadyOrder(input: SubmitReadyOrderInput!): Order! diff --git a/server/api/dadata/address.post.ts b/server/api/dadata/address.post.ts new file mode 100644 index 0000000..a1c3dcf --- /dev/null +++ b/server/api/dadata/address.post.ts @@ -0,0 +1,44 @@ +type DadataAddressSuggestion = { + value: string; + unrestricted_value?: string; + data?: { + fias_id?: string; + }; +}; + +export default defineEventHandler(async (event) => { + const config = useRuntimeConfig(event); + const token = String(config.dadataApiToken || '').trim(); + if (!token) { + throw createError({ + statusCode: 500, + statusMessage: 'DADATA_API_TOKEN is not configured.', + }); + } + + const body = await readBody<{ query?: string }>(event); + const query = String(body?.query ?? '').trim(); + if (query.length < 2) { + return { suggestions: [] as DadataAddressSuggestion[] }; + } + + const response = await $fetch<{ suggestions?: DadataAddressSuggestion[] }>( + 'https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: `Token ${token}`, + }, + body: { + query, + count: 10, + }, + }, + ); + + return { + suggestions: response.suggestions ?? [], + }; +});