Support super manager access
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useQuery } from '@vue/apollo-composable';
|
||||
import { MeDocument } from '~/composables/graphql/generated';
|
||||
import { hasManagerAccess } from '~/utils/roles';
|
||||
|
||||
type NavItem = {
|
||||
to: string;
|
||||
@@ -17,7 +18,7 @@ const centerCapsule = computed<NavItem[]>(() => {
|
||||
{ to: '/orders', label: 'Мои заказы' },
|
||||
];
|
||||
|
||||
if (meQuery.result.value?.me?.role === 'MANAGER') {
|
||||
if (hasManagerAccess(meQuery.result.value?.me?.role)) {
|
||||
items.push(
|
||||
{ to: '/clients', label: 'Пользователи' },
|
||||
{ to: '/client-orders', label: 'Заказы' },
|
||||
|
||||
@@ -198,6 +198,21 @@ export type ManagerUser = {
|
||||
role: UserRole;
|
||||
};
|
||||
|
||||
export type ManagerWithdrawalRequest = {
|
||||
__typename?: 'ManagerWithdrawalRequest';
|
||||
amount: Scalars['Float']['output'];
|
||||
companyName?: Maybe<Scalars['String']['output']>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
requesterEmail: Scalars['String']['output'];
|
||||
requesterFullName: Scalars['String']['output'];
|
||||
requesterId: Scalars['ID']['output'];
|
||||
reviewComment?: Maybe<Scalars['String']['output']>;
|
||||
reviewedById?: Maybe<Scalars['ID']['output']>;
|
||||
status: WithdrawalStatus;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type MessengerConnection = {
|
||||
__typename?: 'MessengerConnection';
|
||||
avatarAvailable: Scalars['Boolean']['output'];
|
||||
@@ -504,6 +519,7 @@ export type Query = {
|
||||
managerNotificationHistory: Array<NotificationHistoryItem>;
|
||||
managerOrders: Array<Order>;
|
||||
managerUsers: Array<ManagerUser>;
|
||||
managerWithdrawalRequests: Array<ManagerWithdrawalRequest>;
|
||||
me?: Maybe<User>;
|
||||
myCart: Cart;
|
||||
myCounterpartyProfile?: Maybe<CounterpartyProfile>;
|
||||
@@ -529,6 +545,11 @@ export type QueryManagerOrdersArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryManagerWithdrawalRequestsArgs = {
|
||||
status?: InputMaybe<WithdrawalStatus>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryMyNotificationHistoryArgs = {
|
||||
channel: MessengerType;
|
||||
limit?: InputMaybe<Scalars['Int']['input']>;
|
||||
@@ -672,7 +693,8 @@ export type User = {
|
||||
|
||||
export enum UserRole {
|
||||
Client = 'CLIENT',
|
||||
Manager = 'MANAGER'
|
||||
Manager = 'MANAGER',
|
||||
SuperManager = 'SUPER_MANAGER'
|
||||
}
|
||||
|
||||
export type VerifyLoginCodeInput = {
|
||||
@@ -829,6 +851,13 @@ export type ManagerUsersQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type ManagerUsersQuery = { __typename?: 'Query', managerUsers: Array<{ __typename?: 'ManagerUser', id: string, email: string, fullName: string, role: UserRole, companyName?: string | null, inn?: string | null, createdAt: any, orderCount: number, lastOrderAt?: any | null }> };
|
||||
|
||||
export type ManagerWithdrawalRequestsQueryVariables = Exact<{
|
||||
status?: InputMaybe<WithdrawalStatus>;
|
||||
}>;
|
||||
|
||||
|
||||
export type ManagerWithdrawalRequestsQuery = { __typename?: 'Query', managerWithdrawalRequests: Array<{ __typename?: 'ManagerWithdrawalRequest', id: string, requesterId: string, requesterEmail: string, requesterFullName: string, companyName?: string | null, amount: number, status: WithdrawalStatus, reviewedById?: string | null, reviewComment?: string | null, createdAt: any, updatedAt: any }> };
|
||||
|
||||
export type ReferralStatsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -1738,6 +1767,46 @@ export function useManagerUsersLazyQuery(options: VueApolloComposable.UseQueryOp
|
||||
return VueApolloComposable.useLazyQuery<ManagerUsersQuery, ManagerUsersQueryVariables>(ManagerUsersDocument, {}, options);
|
||||
}
|
||||
export type ManagerUsersQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<ManagerUsersQuery, ManagerUsersQueryVariables>;
|
||||
export const ManagerWithdrawalRequestsDocument = gql`
|
||||
query ManagerWithdrawalRequests($status: WithdrawalStatus) {
|
||||
managerWithdrawalRequests(status: $status) {
|
||||
id
|
||||
requesterId
|
||||
requesterEmail
|
||||
requesterFullName
|
||||
companyName
|
||||
amount
|
||||
status
|
||||
reviewedById
|
||||
reviewComment
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useManagerWithdrawalRequestsQuery__
|
||||
*
|
||||
* To run a query within a Vue component, call `useManagerWithdrawalRequestsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useManagerWithdrawalRequestsQuery` returns an object from Apollo Client that contains result, loading and error properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param variables that will be passed into the query
|
||||
* @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 } = useManagerWithdrawalRequestsQuery({
|
||||
* status: // value for 'status'
|
||||
* });
|
||||
*/
|
||||
export function useManagerWithdrawalRequestsQuery(variables: ManagerWithdrawalRequestsQueryVariables | VueCompositionApi.Ref<ManagerWithdrawalRequestsQueryVariables> | ReactiveFunction<ManagerWithdrawalRequestsQueryVariables> = {}, options: VueApolloComposable.UseQueryOptions<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>> = {}) {
|
||||
return VueApolloComposable.useQuery<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>(ManagerWithdrawalRequestsDocument, variables, options);
|
||||
}
|
||||
export function useManagerWithdrawalRequestsLazyQuery(variables: ManagerWithdrawalRequestsQueryVariables | VueCompositionApi.Ref<ManagerWithdrawalRequestsQueryVariables> | ReactiveFunction<ManagerWithdrawalRequestsQueryVariables> = {}, options: VueApolloComposable.UseQueryOptions<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>> = {}) {
|
||||
return VueApolloComposable.useLazyQuery<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>(ManagerWithdrawalRequestsDocument, variables, options);
|
||||
}
|
||||
export type ManagerWithdrawalRequestsQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<ManagerWithdrawalRequestsQuery, ManagerWithdrawalRequestsQueryVariables>;
|
||||
export const ReferralStatsDocument = gql`
|
||||
query ReferralStats {
|
||||
referralStats {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MeDocument } from '~/composables/graphql/generated';
|
||||
import { hasManagerAccess } from '~/utils/roles';
|
||||
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const { client } = useApolloClient('default');
|
||||
@@ -7,7 +8,7 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
|
||||
if (response.data.me?.role !== 'MANAGER') {
|
||||
if (!hasManagerAccess(response.data.me?.role)) {
|
||||
return navigateTo('/');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { useQuery } from '@vue/apollo-composable';
|
||||
import {
|
||||
ManagerBonusBalancesDocument,
|
||||
ReferralStatsDocument,
|
||||
ManagerWithdrawalRequestsDocument,
|
||||
type ManagerBonusBalancesQuery,
|
||||
type ReferralStatsQuery,
|
||||
type ManagerWithdrawalRequestsQuery,
|
||||
} from '~/composables/graphql/generated';
|
||||
|
||||
definePageMeta({
|
||||
@@ -12,14 +12,16 @@ definePageMeta({
|
||||
});
|
||||
|
||||
type BalanceItem = ManagerBonusBalancesQuery['managerBonusBalances'][number];
|
||||
type WithdrawalItem = ReferralStatsQuery['referralStats']['pendingWithdrawals'][number];
|
||||
type WithdrawalItem = ManagerWithdrawalRequestsQuery['managerWithdrawalRequests'][number];
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const search = ref('');
|
||||
|
||||
const balancesQuery = useQuery(ManagerBonusBalancesDocument);
|
||||
const bonusQuery = useQuery(ReferralStatsDocument);
|
||||
const withdrawalsQuery = useQuery(ManagerWithdrawalRequestsDocument, {
|
||||
status: 'PENDING',
|
||||
});
|
||||
|
||||
const activeTab = computed<'balances' | 'withdrawals'>(() => (
|
||||
route.query.tab === 'withdrawals' ? 'withdrawals' : 'balances'
|
||||
@@ -35,7 +37,7 @@ function setTab(tab: 'balances' | 'withdrawals') {
|
||||
}
|
||||
|
||||
const balances = computed<BalanceItem[]>(() => balancesQuery.result.value?.managerBonusBalances ?? []);
|
||||
const withdrawals = computed<WithdrawalItem[]>(() => bonusQuery.result.value?.referralStats.pendingWithdrawals ?? []);
|
||||
const withdrawals = computed<WithdrawalItem[]>(() => withdrawalsQuery.result.value?.managerWithdrawalRequests ?? []);
|
||||
|
||||
const filteredBalances = computed(() => {
|
||||
const query = search.value.trim().toLowerCase();
|
||||
@@ -67,7 +69,9 @@ const filteredWithdrawals = computed(() => {
|
||||
}
|
||||
|
||||
return [
|
||||
item.requesterId,
|
||||
item.requesterFullName,
|
||||
item.requesterEmail,
|
||||
item.companyName || '',
|
||||
String(item.amount),
|
||||
item.status,
|
||||
item.reviewComment || '',
|
||||
@@ -133,7 +137,7 @@ const filteredWithdrawals = computed(() => {
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div v-if="bonusQuery.loading.value" class="manager-empty-state">
|
||||
<div v-if="withdrawalsQuery.loading.value" class="manager-empty-state">
|
||||
Загружаем заявки...
|
||||
</div>
|
||||
<div v-else-if="filteredWithdrawals.length === 0" class="manager-empty-state">
|
||||
@@ -147,7 +151,9 @@ const filteredWithdrawals = computed(() => {
|
||||
>
|
||||
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-semibold text-[#123824]">Пользователь: {{ withdrawal.requesterId }}</p>
|
||||
<p class="text-sm font-semibold text-[#123824]">{{ withdrawal.requesterFullName }}</p>
|
||||
<p class="text-sm text-[#355947]">{{ withdrawal.requesterEmail }}</p>
|
||||
<p v-if="withdrawal.companyName" class="text-sm text-[#355947]">{{ withdrawal.companyName }}</p>
|
||||
<p class="text-sm text-[#355947]">Сумма: {{ withdrawal.amount }}</p>
|
||||
<p class="text-xs text-[#5c7b69]">{{ new Date(withdrawal.createdAt).toLocaleString() }}</p>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||||
import {
|
||||
ReferralStatsDocument,
|
||||
ManagerWithdrawalRequestsDocument,
|
||||
ReviewRewardWithdrawalDocument,
|
||||
type ManagerWithdrawalRequestsQuery,
|
||||
} from '~/composables/graphql/generated';
|
||||
|
||||
definePageMeta({
|
||||
@@ -11,8 +12,11 @@ definePageMeta({
|
||||
|
||||
const route = useRoute();
|
||||
const withdrawalId = computed(() => String(route.params.id || ''));
|
||||
type WithdrawalItem = ManagerWithdrawalRequestsQuery['managerWithdrawalRequests'][number];
|
||||
|
||||
const bonusQuery = useQuery(ReferralStatsDocument);
|
||||
const withdrawalsQuery = useQuery(ManagerWithdrawalRequestsDocument, {
|
||||
status: null,
|
||||
});
|
||||
const reviewMutation = useMutation(ReviewRewardWithdrawalDocument);
|
||||
|
||||
const decision = ref<'APPROVE' | 'REJECT'>('APPROVE');
|
||||
@@ -20,7 +24,7 @@ const reviewComment = ref('');
|
||||
const reviewResult = ref('');
|
||||
|
||||
const currentWithdrawal = computed(() =>
|
||||
(bonusQuery.result.value?.referralStats.pendingWithdrawals ?? []).find((item) => item.id === withdrawalId.value),
|
||||
(withdrawalsQuery.result.value?.managerWithdrawalRequests ?? []).find((item: WithdrawalItem) => item.id === withdrawalId.value),
|
||||
);
|
||||
|
||||
async function reviewWithdrawal() {
|
||||
@@ -37,7 +41,7 @@ async function reviewWithdrawal() {
|
||||
});
|
||||
|
||||
reviewResult.value = response?.data?.reviewRewardWithdrawal.status ?? '';
|
||||
await bonusQuery.refetch();
|
||||
await withdrawalsQuery.refetch({ status: null });
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -45,7 +49,7 @@ async function reviewWithdrawal() {
|
||||
<section class="space-y-6 max-w-3xl">
|
||||
<NuxtLink to="/bonus-system" class="text-sm font-semibold text-[#0d854a]">← Назад к бонусам</NuxtLink>
|
||||
|
||||
<div v-if="bonusQuery.loading.value" class="manager-empty-state">
|
||||
<div v-if="withdrawalsQuery.loading.value" class="manager-empty-state">
|
||||
Загружаем заявку на вывод...
|
||||
</div>
|
||||
|
||||
@@ -57,7 +61,9 @@ async function reviewWithdrawal() {
|
||||
<div class="manager-hero">
|
||||
<p class="manager-eyebrow">Вывод</p>
|
||||
<h1 class="manager-title">Проверка заявки на вывод</h1>
|
||||
<p class="manager-copy">Пользователь: {{ currentWithdrawal.requesterId }} · Сумма: {{ currentWithdrawal.amount }}</p>
|
||||
<p class="manager-copy">
|
||||
{{ currentWithdrawal.requesterFullName }} · {{ currentWithdrawal.requesterEmail }} · Сумма: {{ currentWithdrawal.amount }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5 space-y-3">
|
||||
|
||||
5
app/utils/roles.ts
Normal file
5
app/utils/roles.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { UserRole } from '~/composables/graphql/generated';
|
||||
|
||||
export function hasManagerAccess(role?: UserRole | null) {
|
||||
return role === UserRole.Manager || role === UserRole.SuperManager;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
query ManagerWithdrawalRequests($status: WithdrawalStatus) {
|
||||
managerWithdrawalRequests(status: $status) {
|
||||
id
|
||||
requesterId
|
||||
requesterEmail
|
||||
requesterFullName
|
||||
companyName
|
||||
amount
|
||||
status
|
||||
reviewedById
|
||||
reviewComment
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ scalar JSON
|
||||
enum UserRole {
|
||||
CLIENT
|
||||
MANAGER
|
||||
SUPER_MANAGER
|
||||
}
|
||||
|
||||
enum MessengerType {
|
||||
@@ -306,6 +307,20 @@ type ManagerBonusBalance {
|
||||
transactionsCount: Int!
|
||||
}
|
||||
|
||||
type ManagerWithdrawalRequest {
|
||||
id: ID!
|
||||
requesterId: ID!
|
||||
requesterEmail: String!
|
||||
requesterFullName: String!
|
||||
companyName: String
|
||||
amount: Float!
|
||||
status: WithdrawalStatus!
|
||||
reviewedById: ID
|
||||
reviewComment: String
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
}
|
||||
|
||||
type Query {
|
||||
healthcheck: String!
|
||||
me: User
|
||||
@@ -321,6 +336,7 @@ type Query {
|
||||
managerUsers: [ManagerUser!]!
|
||||
managerOrders(status: OrderStatus): [Order!]!
|
||||
managerBonusBalances: [ManagerBonusBalance!]!
|
||||
managerWithdrawalRequests(status: WithdrawalStatus): [ManagerWithdrawalRequest!]!
|
||||
registrationRequests(status: RegistrationStatus): [RegistrationRequest!]!
|
||||
referralStats: ReferralStats!
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user