diff --git a/src/api/tickets.ts b/src/api/tickets.ts index a3fe8dd..57e9213 100644 --- a/src/api/tickets.ts +++ b/src/api/tickets.ts @@ -1,110 +1,104 @@ -import makeRequest from "@root/kitUI/makeRequest"; import { - GetMessagesRequest, - GetMessagesResponse, - GetTicketsRequest, - GetTicketsResponse, - SendTicketMessageRequest, + GetMessagesRequest, + GetMessagesResponse, + GetTicketsRequest, + GetTicketsResponse, + SendTicketMessageRequest, } from "@root/model/ticket"; import { authStore } from "@root/stores/auth"; import ReconnectingEventSource from "reconnecting-eventsource"; -// const { makeRequest } = authStore(); const supportApiUrl = "https://admin.pena.digital/heruvym"; +const makeRequest = authStore.getState().makeRequest; + export function subscribeToAllTickets({ - onMessage, - onError, - accessToken, + onMessage, + onError, + accessToken, }: { - accessToken: string; - onMessage: (e: MessageEvent) => void; - onError: (e: Event) => void; + accessToken: string; + onMessage: (e: MessageEvent) => void; + onError: (e: Event) => void; }) { - const url = `${supportApiUrl}/subscribe?Authorization=${accessToken}`; + const url = `${supportApiUrl}/subscribe?Authorization=${accessToken}`; - const eventSource = createEventSource(onMessage, onError, url); + const eventSource = createEventSource(onMessage, onError, url); - return () => { - eventSource.close(); - }; + return () => { + eventSource.close(); + }; } export function subscribeToTicketMessages({ - onMessage, - onError, - accessToken, - ticketId, + onMessage, + onError, + accessToken, + ticketId, }: { - accessToken: string; - ticketId: string; - onMessage: (e: MessageEvent) => void; - onError: (e: Event) => void; + accessToken: string; + ticketId: string; + onMessage: (e: MessageEvent) => void; + onError: (e: Event) => void; }) { - const url = `${supportApiUrl}/ticket?ticket=${ticketId}&Authorization=${accessToken}`; + const url = `${supportApiUrl}/ticket?ticket=${ticketId}&Authorization=${accessToken}`; - const eventSource = createEventSource(onMessage, onError, url); + const eventSource = createEventSource(onMessage, onError, url); - return () => { - eventSource.close(); - }; + return () => { + eventSource.close(); + }; } export async function getTickets({ - body, - signal, -}: { - body: GetTicketsRequest; - signal: AbortSignal; -}): Promise { - return makeRequest({ - url: `${supportApiUrl}/getTickets`, - method: "POST", - useToken: true, body, signal, - }).then((response) => { - const result = (response as any).data as GetTicketsResponse; - return result; - }); +}: { + body: GetTicketsRequest; + signal: AbortSignal; +}): Promise { + return makeRequest({ + url: `${supportApiUrl}/getTickets`, + method: "POST", + useToken: true, + body, + signal, + }); } export async function getTicketMessages({ - body, - signal, -}: { - body: GetMessagesRequest; - signal: AbortSignal; -}): Promise { - return makeRequest({ - url: `${supportApiUrl}/getMessages`, - method: "POST", - useToken: true, body, signal, - }).then((response) => { - const result = (response as any).data as GetMessagesResponse; - return result; - }); +}: { + body: GetMessagesRequest; + signal: AbortSignal; +}): Promise { + return makeRequest({ + url: `${supportApiUrl}/getMessages`, + method: "POST", + useToken: true, + body, + signal, + }); } -export async function sendTicketMessage({ body }: { body: SendTicketMessageRequest }) { - return makeRequest({ - url: `${supportApiUrl}/send`, - method: "POST", - useToken: true, - body, - }); +export async function sendTicketMessage({ body }: { body: SendTicketMessageRequest; }) { + return makeRequest({ + url: `${supportApiUrl}/send`, + method: "POST", + useToken: true, + body, + }); } function createEventSource(onMessage: (e: MessageEvent) => void, onError: (e: Event) => void, url: string) { - const eventSource = new ReconnectingEventSource(url); + const eventSource = new ReconnectingEventSource(url); - eventSource.addEventListener("open", () => console.log(`EventSource connected with ${url}`)); - eventSource.addEventListener("close", () => console.log(`EventSource closed with ${url}`)); - eventSource.addEventListener("message", onMessage); - eventSource.addEventListener("error", onError); + eventSource.addEventListener("open", () => console.log(`EventSource connected with ${url}`)); + eventSource.addEventListener("close", () => console.log(`EventSource closed with ${url}`)); + eventSource.addEventListener("message", onMessage); + eventSource.addEventListener("error", onError); - return eventSource; + return eventSource; } diff --git a/src/hooks/privilege.hook.ts b/src/hooks/privilege.hook.ts new file mode 100644 index 0000000..c56ce2a --- /dev/null +++ b/src/hooks/privilege.hook.ts @@ -0,0 +1,52 @@ +import { useEffect, useState } from "react"; +import axios from "axios"; + +export type Privilege = { + createdAt: string; + description: string; + isDeleted: boolean; + name: string; + price: string; + privilegeId: string; + serviceKey: string; + type: string; + updatedAt: string; + value: string; + _id: string; +}; + +type UsePrivilegies = { + privilegies: Record<"Шаблонизатор", Privilege[]> | undefined; + isError: boolean; + isLoading: boolean; + errorMessage: string; +}; + +export const usePrivilegies = (): UsePrivilegies => { + const [privilegies, setPrivilegies] = useState>(); + const [isLoading, setIsLoading] = useState(false); + const [isError, setIsError] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); + + useEffect(() => { + const getPrivilegies = async () => { + const { data } = await axios>({ + method: "get", + url: "https://admin.pena.digital/strator/privilege/service", + }); + + return data; + }; + + setIsLoading(true); + getPrivilegies() + .then(setPrivilegies) + .catch(() => { + setIsError(true); + setErrorMessage("Ошибка при получении привилегий"); + }) + .finally(() => setIsLoading(false)); + }, []); + + return { privilegies, isError, isLoading, errorMessage }; +}; diff --git a/src/kitUI/Article.tsx b/src/kitUI/Article.tsx new file mode 100644 index 0000000..83e671b --- /dev/null +++ b/src/kitUI/Article.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Box, Typography } from "@mui/material"; + +type ArticleProps = { + header: JSX.Element; + body: JSX.Element; + isBoby?: boolean; +}; + +export const Article = ({ header, body, isBoby = false }: ArticleProps) => { + return ( + + + {header} + + {isBoby ? {body} : } + + ); +}; diff --git a/src/kitUI/makeRequest.ts b/src/kitUI/makeRequest.ts deleted file mode 100644 index 94b79eb..0000000 --- a/src/kitUI/makeRequest.ts +++ /dev/null @@ -1,58 +0,0 @@ -import axios from "axios"; -interface MakeRequest { - method?: string; - url: string; - body?: unknown; - useToken?: boolean; - contentType?: boolean; - signal?: AbortSignal; -} - -export default (props: MakeRequest) => { - return new Promise(async (resolve, reject) => { - await makeRequest(props) - .then((r) => resolve(r)) - .catch((r) => reject(r)); - }); -}; - -function makeRequest({ method = "post", url, body, useToken = true, signal, contentType = false }: MakeRequest) { - //В случае 401 рефреш должен попробовать вызваться 1 раз - let counterRefresh = true; - let headers: any = {}; - if (useToken) headers["Authorization"] = localStorage.getItem("AT"); - if (contentType) headers["Content-Type"] = "application/json"; - return axios({ - url: url, - method: method, - headers: headers, - data: body, - signal, - }) - .then((response) => { - if (response.data && response.data.accessToken) { - localStorage.setItem("AT", response.data.accessToken); - } - return response; - }) - .catch((error) => { - if (error.response.status == 401 && counterRefresh) { - refresh().then((response) => { - if (response.data && response.data.accessToken) localStorage.setItem("AT", response.data.accessToken); - counterRefresh = false; - }); - } else { - throw error; - } - throw error; - }); -} - -function refresh() { - return axios("https://admin.pena.digital/auth/refresh", { - headers: { - Authorization: localStorage.getItem("AT"), - "Content-Type": "application/json", - }, - }); -} diff --git a/src/model/tariff.ts b/src/model/tariff.ts index ecb9c12..0e726aa 100644 --- a/src/model/tariff.ts +++ b/src/model/tariff.ts @@ -1,46 +1,39 @@ - - export const SERVICE_LIST = [ - { - serviceKey: "templategen", - displayName: "Шаблонизатор документов" - }, - { - serviceKey: "squiz", - displayName: "Опросник" - }, - { - serviceKey: "dwarfener", - displayName: "Сокращатель ссылок" - } + { + serviceKey: "templategen", + displayName: "Шаблонизатор документов", + }, + { + serviceKey: "squiz", + displayName: "Опросник", + }, + { + serviceKey: "dwarfener", + displayName: "Сокращатель ссылок", + }, ] as const; -export type ServiceType = typeof SERVICE_LIST[number]["serviceKey"]; +export type ServiceType = (typeof SERVICE_LIST)[number]["serviceKey"]; -export type PrivilegeType = - | "unlim" - | "gencount" - | "activequiz" - | "abcount" - | "extended"; +export type PrivilegeType = "unlim" | "gencount" | "activequiz" | "abcount" | "extended"; export interface Privilege { - serviceKey: ServiceType; - name: PrivilegeType; - privilegeId: string; - description: string; - /** Единица измерения привелегии: время в днях/кол-во */ - type: "day" | "count"; - /** Стоимость одной единицы привелегии */ - pricePerUnit: number; + serviceKey: ServiceType; + name: PrivilegeType; + privilegeId: string; + description: string; + /** Единица измерения привелегии: время в днях/кол-во */ + type: "day" | "count"; + /** Стоимость одной единицы привелегии */ + pricePerUnit: number; } export interface Tariff { - id: string; - name: string; - privilege: Privilege; - /** Количество единиц привелегии */ - amount: number; - /** Кастомная цена, если есть, то используется вместо privilege.price */ - customPricePerUnit?: number; -} \ No newline at end of file + id: string; + name: string; + privilege: Privilege; + /** Количество единиц привелегии */ + amount: number; + /** Кастомная цена, если есть, то используется вместо privilege.price */ + customPricePerUnit?: number; +} diff --git a/src/pages/Setting/CardPrivilegie.tsx b/src/pages/Setting/CardPrivilegie.tsx new file mode 100644 index 0000000..93812f8 --- /dev/null +++ b/src/pages/Setting/CardPrivilegie.tsx @@ -0,0 +1,117 @@ +import ModeEditOutlineOutlinedIcon from "@mui/icons-material/ModeEditOutlineOutlined"; +import { Box, IconButton, TextField, Tooltip, Typography } from "@mui/material"; +import axios from "axios"; +import { useState } from "react"; + +interface CardPrivilegie { + name: string; + type: string; + price: string; + description: string; +} + +export const СardPrivilegie = ({ name, type, price, description }: CardPrivilegie) => { + const [inputOpen, setInputOpen] = useState(false); + const [inputValue, setInputValue] = useState(""); + + const PutPrivilegies = () => { + axios({ + method: "put", + url: "https://admin.pena.digital/strator/privilege/service", + data: { + price: inputValue, + }, + }); + }; + + const requestOnclickEnter = (event: any) => { + if (event.key === "Enter" && inputValue !== "") { + PutPrivilegies(); + setInputValue(""); + setInputOpen(false); + } + }; + + const onCloseInput = (event: any) => { + if (event.key === "Escape") { + setInputOpen(false); + } + }; + + return ( + + + + + {name} + + + + + + + + + + setInputOpen(!inputOpen)}> + + + + + + {inputOpen ? ( + setInputValue(event.target.value)} + sx={{ + alignItems: "center", + width: "400px", + "& .MuiInputBase-root": { + backgroundColor: "#F2F3F7", + height: "48px", + }, + }} + inputProps={{ + sx: { + borderRadius: "10px", + fontSize: "18px", + lineHeight: "21px", + py: 0, + }, + }} + /> + ) : ( + price: {price} + )} + {type} + + + ); +}; diff --git a/src/pages/Setting/PrivilegiesWrapper.tsx b/src/pages/Setting/PrivilegiesWrapper.tsx new file mode 100644 index 0000000..8dde824 --- /dev/null +++ b/src/pages/Setting/PrivilegiesWrapper.tsx @@ -0,0 +1,139 @@ +import { useState } from "react"; + +import { Box, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"; + +import { СardPrivilegie } from "./CardPrivilegie"; + +import { usePrivilegies } from "@root/hooks/privilege.hook"; + +interface CustomWrapperProps { + text: string; + sx?: SxProps; + result?: boolean; +} + +export const PrivilegiesWrapper = ({ text, sx, result }: CustomWrapperProps) => { + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const upSm = useMediaQuery(theme.breakpoints.up("sm")); + const [isExpanded, setIsExpanded] = useState(false); + + const { privilegies, isError, isLoading, errorMessage } = usePrivilegies(); + + return ( + + + setIsExpanded((prev) => !prev)} + sx={{ + height: "88px", + px: "20px", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + cursor: "pointer", + userSelect: "none", + }} + > + + {text} + + + + {result ? ( + <> + + + + + + + + ) : ( + + + + + )} + + + {isExpanded && + (isError ? ( + errorMessage + ) : ( + privilegies?.Шаблонизатор.map(({ name, type, price, description }) => ( + <СardPrivilegie key={type} name={name} type={type} price={price} description={description} /> + )) + ))} + + + ); +}; diff --git a/src/pages/Setting/SettingRoles.tsx b/src/pages/Setting/SettingRoles.tsx index c402c8a..9a1b854 100644 --- a/src/pages/Setting/SettingRoles.tsx +++ b/src/pages/Setting/SettingRoles.tsx @@ -2,10 +2,11 @@ import { AccordionDetails, Table, TableBody, TableCell, TableHead, TableRow, Typ import FormDeleteRoles from "./FormDeleteRoles"; import FormCreateRoles from "./FormCreateRoles"; +import { PrivilegiesWrapper } from "./PrivilegiesWrapper"; import theme from "../../theme"; -export const SettingRoles = () => { +export const SettingRoles = (): JSX.Element => { return ( {
+
); }; diff --git a/src/pages/dashboard/Content/ServiceUsersDG.tsx b/src/pages/dashboard/Content/ServiceUsersDG.tsx new file mode 100644 index 0000000..e585c7b --- /dev/null +++ b/src/pages/dashboard/Content/ServiceUsersDG.tsx @@ -0,0 +1,45 @@ +import { GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid"; +import { Skeleton } from "@mui/material"; + +import DataGrid from "@kitUI/datagrid"; + +import type { UsersType } from "@root/api/roles"; + +const columns: GridColDef[] = [ + { field: "login", headerName: "Логин", width: 100 }, + { field: "email", headerName: "E-mail", width: 200 }, + { field: "phoneNumber", headerName: "Номер телефона", width: 200 }, + { field: "isDeleted", headerName: "Удалено", width: 100 }, + { field: "createdAt", headerName: "Дата создания", width: 200 }, +]; + +interface Props { + handleSelectionChange: (selectionModel: GridSelectionModel) => void; + users: UsersType | undefined; +} + +export default function ServiceUsersDG({ handleSelectionChange, users }: Props) { + if (!users) { + return Loading...; + } + + const gridData = users.map((user) => ({ + login: user.login, + email: user.email, + phoneNumber: user.phoneNumber, + isDeleted: `${user.isDeleted ? "true" : "false"}`, + createdAt: user.createdAt, + })); + + return ( + users.login} + checkboxSelection={true} + rows={gridData} + columns={columns} + components={{ Toolbar: GridToolbar }} + onSelectionModelChange={handleSelectionChange} + /> + ); +} diff --git a/src/pages/dashboard/Content/Support/Chat/Chat.tsx b/src/pages/dashboard/Content/Support/Chat/Chat.tsx index 9bc51e7..7c54da3 100644 --- a/src/pages/dashboard/Content/Support/Chat/Chat.tsx +++ b/src/pages/dashboard/Content/Support/Chat/Chat.tsx @@ -10,11 +10,11 @@ import { getTicketMessages, sendTicketMessage, subscribeToTicketMessages } from import { enqueueSnackbar } from "notistack"; import { useTicketStore } from "@root/stores/tickets"; import { throttle } from "@root/utils/throttle"; - import { authStore } from "@root/stores/auth"; + export default function Chat() { - const { token } = authStore(); + const token = authStore(state => state.token); const theme = useTheme(); const upMd = useMediaQuery(theme.breakpoints.up("md")); const tickets = useTicketStore(state => state.tickets); @@ -30,13 +30,8 @@ export default function Chat() { const ticket = tickets.find(ticket => ticket.id === ticketId); - useEffect(function scrollOnNewMessage() { - scrollToBottom(); - }, [messages]); - - useEffect( - function fetchTicketMessages() { - if (!ticketId) return; + useEffect(function fetchTicketMessages() { + if (!ticketId) return; const getTicketsBody: GetMessagesRequest = { amt: messagesPerPage, @@ -52,6 +47,7 @@ export default function Chat() { }).then(result => { console.log("GetMessagesResponse", result); if (result?.length > 0) { + if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1) chatBoxRef.current.scrollTop = 1; addOrUpdateMessages(result); setMessageFetchState("idle"); } else setMessageFetchState("all fetched"); @@ -62,39 +58,36 @@ export default function Chat() { return () => { controller.abort(); - clearMessageState(); }; }, [messageApiPage, messagesPerPage, ticketId]); useEffect(function subscribeToMessages() { - if (!ticketId) return; + if (!ticketId || !token) return; - if (!token) return; - - const unsubscribe = subscribeToTicketMessages({ - ticketId, - accessToken: token, - onMessage(event) { - try { - const newMessage = JSON.parse(event.data) as TicketMessage; - console.log("SSE: parsed newMessage:", newMessage); - addOrUpdateMessages([newMessage]); - } catch (error) { - console.log("SSE: couldn't parse:", event.data); - console.log("Error parsing message SSE", error); - } - }, - onError(event) { - console.log("SSE Error:", event); - }, - }); + const unsubscribe = subscribeToTicketMessages({ + ticketId, + accessToken: token, + onMessage(event) { + try { + const newMessage = JSON.parse(event.data) as TicketMessage; + console.log("SSE: parsed newMessage:", newMessage); + addOrUpdateMessages([newMessage]); + } catch (error) { + console.log("SSE: couldn't parse:", event.data); + console.log("Error parsing message SSE", error); + } + }, + onError(event) { + console.log("SSE Error:", event); + }, + }); return () => { unsubscribe(); clearMessageState(); setIsPreventAutoscroll(false); }; - }, [ticketId]); + }, [ticketId, token]); useEffect(function attachScrollHandler() { if (!chatBoxRef.current) return; @@ -108,7 +101,6 @@ export default function Chat() { if (messagesFetchStateRef.current !== "idle") return; if (chatBox.scrollTop < chatBox.clientHeight) { - if (chatBox.scrollTop < 1) chatBox.scrollTop = 1; incrementMessageApiPage(); } }; @@ -159,7 +151,7 @@ export default function Chat() { setMessageField(""); } - function handleAddAttachment() {} + function handleAddAttachment() { } function handleTextfieldKeyPress(e: KeyboardEvent) { if (e.key === "Enter" && !e.shiftKey) { @@ -168,8 +160,6 @@ export default function Chat() { } } - const sortedMessages = messages.sort(sortMessagesByTime); - return ( - {ticket ? - <> - {ticket.title} - - {sortedMessages.map(message => - - )} - - setMessageField(e.target.value)} - onKeyPress={handleTextfieldKeyPress} - id="message-input" - placeholder="Написать сообщение" - fullWidth - multiline - maxRows={8} - InputProps={{ - style: { - backgroundColor: theme.palette.content.main, - color: theme.palette.secondary.main, - }, - endAdornment: ( - - - - - - - - - ) - }} - InputLabelProps={{ - style: { - color: theme.palette.secondary.main, - } - }} - /> - - : - Выберите тикет} + {ticket ? ticket.title : "Выберите тикет"} + + {ticket && messages.map(message => + + )} + + {ticket && + setMessageField(e.target.value)} + onKeyPress={handleTextfieldKeyPress} + id="message-input" + placeholder="Написать сообщение" + fullWidth + multiline + maxRows={8} + InputProps={{ + style: { + backgroundColor: theme.palette.content.main, + color: theme.palette.secondary.main, + }, + endAdornment: ( + + + + + + + + + ) + }} + InputLabelProps={{ + style: { + color: theme.palette.secondary.main, + } + }} + /> + } ); } - -function sortMessagesByTime(message1: TicketMessage, message2: TicketMessage) { - const date1 = new Date(message1.created_at).getTime(); - const date2 = new Date(message2.created_at).getTime(); - return date1 - date2; -} diff --git a/src/pages/dashboard/Content/Support/Support.tsx b/src/pages/dashboard/Content/Support/Support.tsx index 7b4fcdd..f0a810c 100644 --- a/src/pages/dashboard/Content/Support/Support.tsx +++ b/src/pages/dashboard/Content/Support/Support.tsx @@ -7,7 +7,7 @@ import { getTickets, subscribeToAllTickets } from "@root/api/tickets"; import { GetTicketsRequest, Ticket } from "@root/model/ticket"; import { clearTickets, setTicketsFetchState, updateTickets, useTicketStore } from "@root/stores/tickets"; import { enqueueSnackbar } from "notistack"; -import {clearMessageState } from "@root/stores/messages"; +import { clearMessageState } from "@root/stores/messages"; import { authStore } from "@root/stores/auth"; @@ -16,7 +16,7 @@ export default function Support() { const upMd = useMediaQuery(theme.breakpoints.up("md")); const ticketsPerPage = useTicketStore(state => state.ticketsPerPage); const ticketApiPage = useTicketStore(state => state.apiPage); - const { token } = authStore(); + const token = authStore(state => state.token); useEffect(function fetchTickets() { const getTicketsBody: GetTicketsRequest = { @@ -69,7 +69,7 @@ export default function Support() { clearMessageState(); clearTickets(); }; - }, []); + }, [token]); return ( ([]); + const [selectedTariffs, setSelectedTariffs] = useState([]); - return ( - - Список привелегий - - - Список тарифов - setSelectedTariffs(selectionModel)} /> - - - ); -} \ No newline at end of file + return ( + + Список привелегий + + + + Список тарифов + + setSelectedTariffs(selectionModel)} /> + + + ); +} diff --git a/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx b/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx index 40f9992..2413580 100644 --- a/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx +++ b/src/pages/dashboard/Content/Tariffs/tariffsDG.tsx @@ -1,48 +1,49 @@ import * as React from "react"; import { GridColDef, GridSelectionModel, GridToolbar } from "@mui/x-data-grid"; + import DataGrid from "@kitUI/datagrid"; + import { useTariffStore } from "@root/stores/tariffs"; import { SERVICE_LIST } from "@root/model/tariff"; - const columns: GridColDef[] = [ - { field: 'id', headerName: 'ID', width: 100 }, - { field: 'name', headerName: 'Название тарифа', width: 150 }, - { field: 'serviceName', headerName: 'Сервис', width: 150 },//инфо из гитлаба. - { field: 'privilege', headerName: 'Привелегия', width: 150 }, - { field: 'amount', headerName: 'Количество', width: 110 }, - { field: 'type', headerName: 'Единица', width: 100 }, - { field: 'pricePerUnit', headerName: 'Цена за ед.', width: 100 }, - { field: 'isCustomPrice', headerName: 'Кастомная цена', width: 130 }, - { field: 'total', headerName: 'Сумма', width: 130 }, + { field: "id", headerName: "ID", width: 100 }, + { field: "name", headerName: "Название тарифа", width: 150 }, + { field: "serviceName", headerName: "Сервис", width: 150 }, //инфо из гитлаба. + { field: "privilege", headerName: "Привелегия", width: 150 }, + { field: "amount", headerName: "Количество", width: 110 }, + { field: "type", headerName: "Единица", width: 100 }, + { field: "pricePerUnit", headerName: "Цена за ед.", width: 100 }, + { field: "isCustomPrice", headerName: "Кастомная цена", width: 130 }, + { field: "total", headerName: "Сумма", width: 130 }, ]; interface Props { - handleSelectionChange: (selectionModel: GridSelectionModel) => void; + handleSelectionChange: (selectionModel: GridSelectionModel) => void; } export default function TariffsDG({ handleSelectionChange }: Props) { - const tariffs = useTariffStore(state => state.tariffs); + const tariffs = useTariffStore((state) => state.tariffs); - const gridData = tariffs.map(tariff => ({ - id: tariff.id, - name: tariff.name, - serviceName: SERVICE_LIST.find(service => service.serviceKey === tariff.privilege.serviceKey)?.displayName, - privilege: `(${tariff.privilege.privilegeId}) ${tariff.privilege.description}`, - amount: tariff.amount, - type: tariff.privilege.type === "count" ? "день" : "шт.", - pricePerUnit: tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit, - isCustomPrice: tariff.customPricePerUnit === undefined ? "Нет" : "Да", - total: tariff.amount * (tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit), - })); + const gridData = tariffs.map((tariff) => ({ + id: tariff.id, + name: tariff.name, + serviceName: SERVICE_LIST.find((service) => service.serviceKey === tariff.privilege.serviceKey)?.displayName, + privilege: `(${tariff.privilege.privilegeId}) ${tariff.privilege.description}`, + amount: tariff.amount, + type: tariff.privilege.type === "count" ? "день" : "шт.", + pricePerUnit: tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit, + isCustomPrice: tariff.customPricePerUnit === undefined ? "Нет" : "Да", + total: tariff.amount * (tariff.customPricePerUnit ?? tariff.privilege.pricePerUnit), + })); - return ( - - ); + return ( + + ); } diff --git a/src/pages/dashboard/Content/Users.tsx b/src/pages/dashboard/Content/Users.tsx index a281b26..8c3bd0f 100644 --- a/src/pages/dashboard/Content/Users.tsx +++ b/src/pages/dashboard/Content/Users.tsx @@ -1,35 +1,46 @@ import * as React from "react"; +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; -import { Box, Typography, TextField, Button } from "@mui/material"; -import Table from "@mui/material/Table"; -import TableHead from "@mui/material/TableHead"; -import TableBody from "@mui/material/TableBody"; -import TableCell from "@mui/material/TableCell"; -import TableRow from "@mui/material/TableRow"; -import Radio from "@mui/material/Radio"; -import Skeleton from "@mui/material/Skeleton"; -import Accordion from "@mui/material/Accordion"; -import AccordionSummary from "@mui/material/AccordionSummary"; -import AccordionDetails from "@mui/material/AccordionDetails"; +import axios from "axios"; +import { + AccordionDetails, + AccordionSummary, + Accordion, + Skeleton, + Radio, + Box, + Typography, + TextField, + Button, + Table, + TableHead, + TableBody, + TableCell, + TableRow, +} from "@mui/material"; +import { GridSelectionModel } from "@mui/x-data-grid"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ClearIcon from "@mui/icons-material/Clear"; + +import ConditionalRender from "@root/pages/Setting/ConditionalRender"; +import ServiceUsersDG from "./ServiceUsersDG"; + +import { authStore } from "@stores/auth"; + import { getRoles_mock, TMockData } from "../../../api/roles"; +import theme from "../../../theme"; + import type { UsersType } from "../../../api/roles"; -import theme from "../../../theme"; -import axios from "axios"; -import {authStore} from "@stores/auth"; -import ConditionalRender from "@root/pages/Setting/ConditionalRender"; - const Users: React.FC = () => { - const { makeRequest } = authStore(); - // makeRequest({ - // url: "https://admin.pena.digital/strator/account", - // method: "get", - // bearer: true, - // contentType: true, - // }) + const { makeRequest } = authStore(); + // makeRequest({ + // url: "https://admin.pena.digital/strator/account", + // method: "get", + // bearer: true, + // contentType: true, + // }) const radioboxes = ["admin", "manager", "user"]; const [selectedValue, setSelectedValue] = React.useState("admin"); @@ -75,35 +86,48 @@ const Users: React.FC = () => { const [users, setUsers] = React.useState(); const [manager, setManager] = React.useState(); - React.useEffect(() => { - const axiosRoles = async () => { - const { data } = await axios({ - method: "get", - url: "https://admin.pena.digital/strator/role/", - }); - setRoles(data); - }; - const gettingRegisteredUsers = async () => { - const { data } = await axios({ - method: "get", - url: "https://hub.pena.digital/user/", - }); - setUsers(data); - }; + useEffect(() => { + async function axiosRoles() { + try { + const { data } = await axios({ + method: "get", + url: "https://admin.pena.digital/strator/role/", + }); + setRoles(data); + } catch (error) { + console.error("Ошибка при получении ролей!"); + } + } + async function gettingRegisteredUsers() { + try { + const { data } = await axios({ + method: "get", + url: "https://hub.pena.digital/user/", + }); + setUsers(data); + } catch (error) { + console.error("Ошибка при получении пользователей!"); + } + } - const gettingListManagers = async () => { - const { data } = await axios({ - method: "get", - url: "https://admin.pena.digital/user/", - }); - setManager(data); - }; + async function gettingListManagers() { + try { + const { data } = await axios({ + method: "get", + url: "https://hub.pena.digital/user/", + }); + setManager(data); + } catch (error) { + console.error("Ошибка при получении менеджеров!"); + } + } gettingListManagers(); gettingRegisteredUsers(); axiosRoles(); - }, []); + }, [selectedValue]); + const [selectedTariffs, setSelectedTariffs] = useState([]); return (