├── public ├── icons │ ├── can.png │ └── hash.png └── index.html ├── src ├── utils │ ├── config.js │ └── api.js ├── components │ ├── Spinner.js │ ├── Agents │ │ ├── AgentsList.js │ │ ├── RatioModal.js │ │ ├── ChatingModal.js │ │ ├── WorkingModal.js │ │ ├── Agent.js │ │ └── AgentDetails.js │ └── index.js └── index.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /public/icons/can.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livechat/supervisor-app/HEAD/public/icons/can.png -------------------------------------------------------------------------------- /public/icons/hash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livechat/supervisor-app/HEAD/public/icons/hash.png -------------------------------------------------------------------------------- /src/utils/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | client_id: "", 3 | server_url: 4 | "https://us-central1-livechat-experiments.cloudfunctions.net/restApi", 5 | account_url: "https://accounts.livechatinc.com/", 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /src/components/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Loader from "react-loader-spinner"; 3 | 4 | export default ({ marginTop }) => ( 5 |
12 | 13 |
14 | ); 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | React App 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/Agents/AgentsList.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Agent from "./Agent"; 3 | import Spinner from "../Spinner"; 4 | import "styled-components/macro"; 5 | 6 | const noResultsStyle = ` 7 | margin: auto; 8 | `; 9 | 10 | const AgentList = ({ agents = [], loading, accessToken }) => { 11 | if (loading) { 12 | return ; 13 | } 14 | 15 | if (agents.length <= 0) { 16 | return
No results
; 17 | } 18 | 19 | return agents.map((agent, i) => ( 20 | 21 | )); 22 | }; 23 | 24 | export default AgentList; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supervisor", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@livechat/accounts-sdk": "^2.0.6", 7 | "@livechat/design-system": "^0.1.3", 8 | "@material-ui/core": "^3.9.3", 9 | "axios": "^1.3.5", 10 | "material-icons-react": "^1.0.4", 11 | "react": "^16.8.6", 12 | "react-dom": "^16.8.6", 13 | "react-loader-spinner": "^2.3.0", 14 | "react-scripts": "^2.1.8", 15 | "recharts": "^1.5.0", 16 | "styled-components": "^4.2.0" 17 | }, 18 | "scripts": { 19 | "start": "HTTPS=true react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/api.js: -------------------------------------------------------------------------------- 1 | import config from "./config"; 2 | import axios from "axios"; 3 | 4 | const GET = "GET"; 5 | const { server_url } = config; 6 | 7 | const createApiRequest = (method, route, accessToken, login) => { 8 | return axios({ 9 | method, 10 | url: server_url + route, 11 | headers: { 12 | Authorization: "Bearer " + accessToken, 13 | DateInterval: 1, 14 | "X-API-Version": "2", 15 | Agent: login 16 | } 17 | }).catch(function(error) { 18 | console.error(error); 19 | }); 20 | }; 21 | 22 | const api = { 23 | fetchAgents: accessToken => createApiRequest(GET, "/agents", accessToken), 24 | fetchAgentRatings: (login, accessToken) => 25 | createApiRequest(GET, "/ratings/week", accessToken, login), 26 | fetchAgentAvailability: (login, accessToken) => 27 | createApiRequest(GET, "/availability", accessToken, login), 28 | fetchChattingTime: (login, accessToken) => 29 | createApiRequest(GET, "/chatting", accessToken, login) 30 | }; 31 | 32 | export default api; 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 LiveChat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/Agents/RatioModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip } from "recharts"; 3 | 4 | import Spinner from "../Spinner"; 5 | import "styled-components/macro"; 6 | 7 | const conatinerStyle = ` 8 | display: grid; 9 | grid-gap: 20px; 10 | justify-items: center; 11 | `; 12 | 13 | export default ({ data }) => { 14 | if (!data || Object.keys(data).length === 0) { 15 | return ; 16 | } 17 | const newData = Object.keys(data).map((e) => ({ 18 | day: e, 19 | good: data[e].good, 20 | bad: data[e].bad, 21 | chats: data[e].chats, 22 | })); 23 | 24 | const sum = (key) => newData.reduce((a, b) => a + (b[key] || 0), 0); 25 | 26 | const allGood = sum("good"); 27 | const allBad = sum("bad"); 28 | const allChats = sum("chats"); 29 | 30 | return ( 31 |
32 | Agent Ratings 33 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 | ); 51 | }; 52 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, Fragment } from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { createGlobalStyle } from "styled-components"; 4 | import AccountsSDK from "@livechat/accounts-sdk"; 5 | import config from "./utils/config"; 6 | import App from "./components"; 7 | import Spinner from "./components/Spinner"; 8 | import "@livechat/design-system/dist/design-system.css"; 9 | 10 | const GlobalStyle = createGlobalStyle` 11 | ::-webkit-scrollbar { 12 | display: none; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | padding: 0; 18 | font-family: Arial, Helvetica, sans-serif; 19 | overflow-x: hidden; 20 | } 21 | `; 22 | 23 | const useLiveChat = ({ client_id, account_url }) => { 24 | const [accessToken, setAccessToken] = useState(null); 25 | 26 | const accountsSDK = new AccountsSDK({ 27 | client_id: client_id, 28 | server_url: account_url, 29 | }); 30 | 31 | useEffect(() => { 32 | const authorize = async () => { 33 | try { 34 | const authorizeData = await accountsSDK.redirect().authorizeData(); 35 | 36 | accountsSDK.verify(authorizeData); 37 | const { access_token } = authorizeData; 38 | setAccessToken(access_token); 39 | } catch (error) { 40 | await accountsSDK.redirect().authorize(); 41 | } 42 | }; 43 | authorize(); 44 | }, []); 45 | 46 | return [accessToken]; 47 | }; 48 | 49 | const AppWithAuth = () => { 50 | const [accessToken] = useLiveChat(config); 51 | 52 | if (!accessToken) { 53 | return ; 54 | } 55 | 56 | return ( 57 | 58 | 59 | 60 | 61 | ); 62 | }; 63 | 64 | ReactDOM.render(, document.getElementById("root")); 65 | -------------------------------------------------------------------------------- /src/components/Agents/ChatingModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | AreaChart, 4 | Area, 5 | XAxis, 6 | YAxis, 7 | CartesianGrid, 8 | Tooltip 9 | } from "recharts"; 10 | 11 | import "styled-components/macro"; 12 | import Spinner from "../Spinner"; 13 | 14 | export default ({ data }) => { 15 | if (!data) { 16 | return ; 17 | } 18 | 19 | const chartData = Object.keys(data).map(e => ({ 20 | name: e.substr(5), 21 | hours: data[e].hours 22 | })); 23 | 24 | const sum = chartData.reduce( 25 | (previous, current) => 26 | (current.hours += previous.hours ? previous.hours : 0) 27 | ); 28 | const avg = sum / chartData.length; 29 | const longest = chartData.reduce((a, b) => (a.hours > b.hours ? a : b)).hours; 30 | const shortest = chartData.reduce((a, b) => (a.hours < b.hours ? a : b)) 31 | .hours; 32 | 33 | return ( 34 |
41 | Agent Chating Time 42 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | Average Time: {avg} h 65 | Longest Time: {longest} h 66 | Shortest Time: {shortest} h 67 |
68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/Agents/WorkingModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | AreaChart, 4 | Area, 5 | XAxis, 6 | YAxis, 7 | CartesianGrid, 8 | Tooltip 9 | } from "recharts"; 10 | 11 | import Spinner from "../Spinner"; 12 | import "styled-components/macro"; 13 | 14 | const containerStyle = ` 15 | display: grid; 16 | grid-gap: 20px; 17 | justify-items: center; 18 | `; 19 | 20 | export default ({ data }) => { 21 | if (!data || Object.keys(data).length === 0) { 22 | return ; 23 | } 24 | 25 | const chartData = Object.keys(data).map(e => ({ 26 | name: e.substr(5), 27 | hours: data[e].hours 28 | })); 29 | const sum = chartData.reduce( 30 | (previous, current) => 31 | (current.hours += previous.hours ? previous.hours : 0) 32 | ); 33 | const average = sum / chartData.length; 34 | const longest = chartData.reduce((a, b) => (a.hours > b.hours ? a : b)).hours; 35 | const shortest = chartData.reduce((a, b) => (a.hours < b.hours ? a : b)) 36 | .hours; 37 | 38 | return ( 39 |
40 | Agent Working Time 41 | 52 | 53 | 54 | 55 | 56 | 62 | 63 | Average Time: {average} h 64 | Longest Time: {longest} h 65 | Shortest Time: {shortest} h 66 |
67 | ); 68 | }; 69 | -------------------------------------------------------------------------------- /src/components/Agents/Agent.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Toast } from "@livechat/design-system"; 3 | import MaterialIcon from "material-icons-react"; 4 | import AgentDetails from "./AgentDetails"; 5 | import "styled-components/macro"; 6 | 7 | const WrapperStyle = ` 8 | .lc-toast { 9 | width: 100%; 10 | box-shadow: none; 11 | border: 1px solid #E4E8EC; 12 | } 13 | `; 14 | 15 | const ToastStyle = ` 16 | cursor: pointer; 17 | .lc-toast__content { 18 | height: 100%; 19 | width: 100%; 20 | display: grid; 21 | grid-gap: 20px; 22 | grid-template: "avatar name info" / 25px auto 40px; 23 | align-items: center; 24 | } 25 | .lc-toast__icon { 26 | display: none; 27 | } 28 | 29 | :hover { 30 | background-color: hsl(0, 0%, 97%); 31 | } 32 | `; 33 | 34 | const AvaratStyle = (status) => ` 35 | grid-area: avatar; 36 | width: 30px; 37 | border-radius: 20px; 38 | border: ${ 39 | status === "accepting chats" 40 | ? "solid #4bb678 2px" 41 | : "solid hsl(0, 0%, 85%) 2px" 42 | }; 43 | `; 44 | 45 | const NameStyle = ` 46 | grid-area: name; 47 | font-size: 16px; 48 | `; 49 | 50 | const ArrowStyle = ` 51 | grid-area: info; 52 | justify-self: end; 53 | width: 20px; 54 | margin-right: 10px; 55 | `; 56 | 57 | const Agent = ({ agentData = [], accessToken }) => { 58 | const [showDetails, setShowDetails] = useState(false); 59 | const { avatar, name, status, login, permission } = agentData; 60 | 61 | return ( 62 |
63 | { 66 | setShowDetails(!showDetails); 67 | }} 68 | > 69 | avatar 74 | {name} 75 | 76 | {showDetails ? ( 77 | 78 | 79 | 80 | ) : ( 81 | 82 | )} 83 | 84 | 85 | {showDetails && ( 86 | 93 | )} 94 |
95 | ); 96 | }; 97 | 98 | export default Agent; 99 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useMemo, useRef } from "react"; 2 | import { 3 | TabsWrapper, 4 | TabsList, 5 | Tab, 6 | InputField, 7 | } from "@livechat/design-system"; 8 | import "styled-components/macro"; 9 | import MaterialIcon from "material-icons-react"; 10 | import api from "../utils/api"; 11 | import AgentList from "./Agents/AgentsList"; 12 | 13 | const mainConatinerStyle = ` 14 | margin-left: auto; 15 | margin-right: auto; 16 | height: 100%; 17 | display: grid; 18 | grid-template-rows: 50px 40px auto; 19 | grid-gap: 10px; 20 | padding: 5px; 21 | border-radius: 8px; 22 | max-width: 500px; 23 | `; 24 | 25 | const tabsStyle = ` 26 | background-color: white; 27 | border: solid 1px hsl(0, 0%, 90%); 28 | `; 29 | 30 | const tabStyle = ` 31 | display: flex; 32 | align-items: center; 33 | `; 34 | 35 | const labelStyle = ` 36 | margin-left: 7px; 37 | `; 38 | 39 | const tabs = [ 40 | { title: "All", icon: "supervised_user_circle" }, 41 | { title: "Online", icon: "fiber_manual_record" }, 42 | { title: "Offline", icon: "not_interested" }, 43 | ]; 44 | 45 | const useInterval = (callback, delay) => { 46 | const savedCallback = useRef(); 47 | 48 | useEffect(() => { 49 | savedCallback.current = callback; 50 | }, [callback]); 51 | 52 | useEffect(() => { 53 | function tick() { 54 | savedCallback.current(); 55 | } 56 | if (delay !== null) { 57 | let id = setInterval(tick, delay); 58 | return () => clearInterval(id); 59 | } 60 | }, [delay]); 61 | }; 62 | 63 | const App = ({ accessToken }) => { 64 | const [tabId, setTabId] = useState("All"); 65 | const [agents, setAgents] = useState([]); 66 | const [searchValue, setSearchValue] = useState(""); 67 | const [loading, setLoading] = useState(false); 68 | 69 | const fetchAgents = () => { 70 | setLoading(true); 71 | api 72 | .fetchAgents(accessToken) 73 | .then((response) => setAgents(response.data)) 74 | .finally(() => setLoading(false)); 75 | }; 76 | 77 | useInterval(() => { 78 | fetchAgents(); 79 | }, 60000); 80 | 81 | useEffect(() => { 82 | fetchAgents(); 83 | }, []); 84 | 85 | const renderTabs = () => 86 | tabs.map((tab) => { 87 | const { title, icon } = tab; 88 | return ( 89 | { 92 | setTabId(title); 93 | fetchAgents(); 94 | }} 95 | isSelected={title === tabId} 96 | > 97 |
98 | 99 | {title} 100 |
101 |
102 | ); 103 | }); 104 | 105 | const memoizedTabs = useMemo(() => renderTabs(), [tabId]); 106 | 107 | let filteredAgents = []; 108 | 109 | switch (tabId) { 110 | case "All": 111 | filteredAgents = agents; 112 | break; 113 | case "Online": 114 | filteredAgents = agents.filter((e) => e.status === "accepting chats"); 115 | break; 116 | case "Offline": 117 | filteredAgents = agents.filter((e) => e.status !== "accepting chats"); 118 | break; 119 | default: 120 | console.error(`Unexpected tabId: ${tabId}`); 121 | } 122 | 123 | if (searchValue) { 124 | filteredAgents = filteredAgents.filter((e) => 125 | e.name.toLowerCase().includes(searchValue.toLowerCase()) 126 | ); 127 | } 128 | 129 | return ( 130 |
131 |
132 | 133 | {memoizedTabs} 134 | 135 |
136 | setSearchValue(e.target.value)} 142 | style={{ width: "100%", borderColor: "hsl(0, 0%, 85%)" }} 143 | /> 144 | 149 |
150 | ); 151 | }; 152 | 153 | export default App; 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://i.ibb.co/J5CxbdT/supervisor-icon.png) 2 | 3 | # Supervisor 4 | 5 | **Supervisor** is a simple widget helping you monitor the weekly progress of your agents and their availability. With **Supervisor**, you can check the list of available agents. Also, it shows the agents' basic statistics from the last 10 days. 6 | 7 | The statistics include the following information: 8 | 9 | - [Agent working time](https://platform.text.com/docs/data-reporting/reports-api#availability) 10 | - [Chatting time](https://platform.text.com/docs/data-reporting/reports-api/v2.0#chatting-time) 11 | - [Agent ratings](https://platform.text.com/docs/data-reporting/reports-api#ratings) 12 | 13 | ## Preview 14 | 15 | ![Alt Text](https://i.ibb.co/rfnHsTz/supervisor.png) 16 | 17 | # App setup 18 | 19 | ## Before you start 20 | 21 | To use this application in your LiveChat dashboard, you'll need to create your own app in [Developer Console](https://platform.text.com/console) and get the **Client Id**. 22 | 23 | ## Getting started 24 | 25 | 1. Go to [Apps](https://platform.text.com/console/apps) in [Developer Console](https://platform.text.com/console). 26 | 2. Click **New App** and give it an **App Name**. 27 | 3. Choose the **LiveChat** product as the product you want to build for. 28 | 4. Go to **Building Blocks**. 29 | 5. Add **App Authorization** and mark it as _JavaScript App_. Your **Client Id** will be displayed there. 30 | 6. Add `agents--all:ro` and `reports_read` scopes to the App scopes and API access box. 31 | 7. Fetch the **Supervisor** app repository. 32 | 8. In the app directory, do the following steps : 33 | 34 | - Install dependencies (`npm install`). 35 | - In your project, go to `src/utils/congif.js` and replace `client_id` with your own **Client Id** (the one from **Step 5**). 36 | - Run your app (`npm start`). 37 | 38 | 9. Add your app url (for example: `https://localhost:3000`) in these two locations: 39 | 40 | - **Redirect URI whitelist** 41 | - **Agent App Widgets** 42 | 43 | 10. In **Private installation**, click **Install app**. 44 | 45 | You should now be able to use **Supervisor** with LiveChat. 46 | 47 | # How it works 48 | 49 | [Agent App Widgets](https://platform.text.com/docs/extending-agent-app) are web applications loaded inside the LiveChat Agent App. All agents can interact with the widget during chats with customers. The widget itself is displayed in the Agent’s App sidebar. 50 | 51 | To get information such as tags, you need to use [Configuration API](https://platform.text.com/docs/management/configuration-api). 52 | 53 | In order to pull data from our server, you need to include an **access_token** in all the requests. You can get it using one of the [agent authorization flows](https://platform.text.com/docs/authorization/agent-authorization). 54 | 55 | # More sample apps 56 | 57 | Experiment more with our different sample apps: 58 | 59 | - [Tag Master](https://github.com/livechat/tag-master) - widget for helping your agents manage tags and canned responses. 60 | - [Progress](https://github.com/livechat/progress-app) - widget for helping you monitor statistics of your team, such as chat ratings, response times, and chatting times. 61 | - [Sample Redirect App with the redirect authorization flow](https://github.com/livechat/sample-app-redirect-auth) 62 | - [Sample Popup App with the popup authorization flow](https://github.com/livechat/sample-app-popup-auth) 63 | 64 | # Feedback 65 | 66 | If you find some bugs, please create an issue in this repo. We will try to fix it ASAP ;) 67 | 68 | # [Text Platform](https://platform.text.com/): who are we? 69 | 70 | Behind [Text](https://www.text.com/), there’s a [team of passionate people](https://www.text.com/team/) building online customer service software with online chat, help desk software, chatbot, and web analytics capabilities. 71 | 72 | With a suite of five products ([LiveChat](https://www.livechat.com), [ChatBot](https://chatbot.com/), [HelpDesk](https://helpdesk.com/), [KnowledgeBase](https://www.knowledgebase.com/), and [OpenWidget](https://openwidget.com/)) and their powerful APIs, we power customer communication for 36,000 companies in 150 countries. 73 | 74 | [The Platform](https://platform.text.com/) is a range of products and services that can be used to build a variety of communication tools for businesses. Our [Developer Program](https://platform.text.com/developer-program) and [Marketplace](https://www.livechat.com/marketplace/) create an open ecosystem for developers, partners, and customers. With our [advanced APIs](https://platform.text.com/) and comprehensive [documentation](https://platform.text.com/docs), you can shape the future of communication with us — starting today. 75 | 76 | [Join our Discord](https://discord.com/invite/NcfJu3a9kM) to learn, get inspired, and meet other developers! 77 | 78 | --- 79 | 80 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 81 | -------------------------------------------------------------------------------- /src/components/Agents/AgentDetails.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Button, ModalBase } from "@livechat/design-system"; 3 | import MaterialIcon from "material-icons-react"; 4 | import RatioModal from "./RatioModal"; 5 | import ChatingModal from "./ChatingModal"; 6 | import WorkingModal from "./WorkingModal"; 7 | import api from "../../utils/api"; 8 | import "styled-components/macro"; 9 | 10 | const containerStyle = ` 11 | width: calc(100% - 22px); 12 | height: auto; 13 | background-color: white; 14 | display: grid; 15 | grid-template: "login login login " 30px 16 | "perm perm perm" 30px 17 | "status status status" 30px 18 | "line line line" 1px 19 | "space space space" 45px 20 | "btn btn btn" 35px; 21 | grid-gap: 5px; 22 | padding: 20px 10px; 23 | color: hsl(0, 0%, 45%); 24 | border: 1px solid #E4E8EC; 25 | border-radius: 4px; 26 | `; 27 | 28 | const rowStyle = (area) => ` 29 | grid-area: ${area}; 30 | display: flex; 31 | align-items: center; 32 | padding-left: 5px; 33 | 34 | > span { 35 | margin-left: 10px; 36 | font-size: 14px; 37 | } 38 | `; 39 | 40 | const buttonStyle = (area) => ` 41 | grid-area: ${area}; 42 | height: 30px; 43 | width: 80px; 44 | `; 45 | 46 | const lineStyle = (area) => ` 47 | grid-area: ${area}; 48 | height: 1px; 49 | width: 100%; 50 | background-color: hsl(0, 0%, 90%); 51 | margin-top: 12px; 52 | `; 53 | 54 | const spaceStyle = (area) => ` 55 | grid-area: ${area}; 56 | font-size: 12px; 57 | line-height: 16px; 58 | color: #677179; 59 | padding-top: 25px; 60 | `; 61 | 62 | export default ({ login, permission, status, name, accessToken }) => { 63 | const [modals, setModals] = useState([false, false, false]); 64 | const [agentRatings, setAgentRatings] = useState({}); 65 | const [agentAvailability, setAgentAvailability] = useState({}); 66 | const [agentChattingTime, setAgentChattingTime] = useState({}); 67 | 68 | const fetchAgentRatings = () => 69 | api 70 | .fetchAgentRatings(login, accessToken) 71 | .then((response) => setAgentRatings(response.data)) 72 | .catch((error) => console.log(error)); 73 | 74 | const fetchAgentAvailability = () => 75 | api 76 | .fetchAgentAvailability(login, accessToken) 77 | .then((response) => setAgentAvailability(response.data)) 78 | .catch((error) => console.log(error)); 79 | 80 | const fetchChattingTime = () => 81 | api 82 | .fetchChattingTime(login, accessToken) 83 | .then((response) => setAgentChattingTime(response.data)) 84 | .catch((error) => console.log(error)); 85 | 86 | useEffect(() => { 87 | fetchAgentRatings(); 88 | fetchAgentAvailability(); 89 | fetchChattingTime(); 90 | }, []); 91 | 92 | const handleModal = (i) => { 93 | const newModals = [...modals]; 94 | newModals[i] = !modals[i]; 95 | setModals(newModals); 96 | }; 97 | 98 | const renderChart = (type) => { 99 | switch (type) { 100 | case "Working": 101 | return ; 102 | case "Chating": 103 | return ; 104 | case "Ratio": 105 | return ; 106 | default: 107 | console.error(`Unexpected type: ${type}`); 108 | } 109 | }; 110 | 111 | return ( 112 |
113 | 114 | 115 | {login} 116 | 117 | 118 | 119 | {permission} 120 | 121 | 122 | {status === "accepting chats" ? ( 123 | 124 | ) : ( 125 | 129 | )} 130 | {status} 131 | 132 |
133 |
Available charts:
134 | {["Working", "Chating", "Ratio"].map((e, i) => { 135 | return ( 136 |
142 | 145 | {modals[i] && ( 146 | handleModal(i)} 148 | style={{ width: "600px", height: "450px" }} 149 | > 150 |
{renderChart(e, name)}
151 |
152 | )} 153 |
154 | ); 155 | })} 156 |
157 | ); 158 | }; 159 | --------------------------------------------------------------------------------