import React, { Component } from "react"; import firebase from "firebase/app"; import "firebase/auth"; import "firebase/firestore"; import { Button, Card, Table, Tabs, Tag, Switch, Input, notification, Divider, Row, Col, PageHeader, InputNumber, Descriptions, Badge, Collapse, Modal, Select, } from "antd"; import { getPartyDashboardData, getServerDashboardData, postUpdateVersionActive, postUpdateVideoSelectors, postUpdateVideoNameSelectors, postSendMessage, } from "./Api"; import { CheckCircleTwoTone, CloseCircleTwoTone, ReloadOutlined, LoadingOutlined, EnterOutlined, DeleteOutlined, PlusCircleFilled, SaveFilled, SaveOutlined, LogoutOutlined, SendOutlined, PlusCircleOutlined, } from "@ant-design/icons"; const PLAYER_SOURCES = [ { text: "Netflix", value: "netflix." }, { text: "Amazon", value: "amazon." }, { text: "Hulu", value: "hulu." }, { text: "Disney+", value: "disneyplus." }, { text: "YouTube", value: "youtube." }, { text: "HBO Now", value: "hbonow." }, { text: "HBO Max", value: "hbomax." }, { text: "Peacock", value: "peacocktv." }, { text: "Google Play", value: "play.google." }, { text: "Crunchyroll", value: "crunchyroll." }, ]; const MESSAGE_TYPES = [ { text: "REPLY", value: "interactive/text", }, { text: "LINK", value: "interactive/link", }, { text: "MESSAGE", value: "", }, ]; const emptyMessage = { type: "", text: "", uid: "", name: "", endpoint: "", // time set on send, }; export default class Dashboard extends Component { constructor(props) { super(props); this.state = { partiesOverview: null, activeTab: "1", refreshing: true, selectedParties: [], nameSelectors: {}, selectors: {}, versions: [], activeLimit: 5, afkLimit: 10, messageModel: false, newMessage: emptyMessage, }; this.serverStatusListener = null; } componentDidMount() { this.getPartiesOverview(); this.getVersionsOverview(); } getPartiesOverview = async () => { getPartyDashboardData() .then((res) => res.json()) .then((res) => { this.setState({ partiesOverview: res.partyData, refreshing: false }); }); }; getVersionsOverview = () => { getServerDashboardData() .then((res) => res.json()) .then((res) => this.setState({ ...res.serverData, refreshing: false })); }; refreshData = () => { this.setState({ refreshing: true }); const { activeTab } = this.state; switch (activeTab) { case "1": this.getPartiesOverview(); break; case "2": case "3": case "4": this.getVersionsOverview(); break; default: break; } }; tabChange = (activeTab) => { this.setState({ activeTab }); }; updateVersionMessage = (message, version) => { this.setState({ refreshing: true }); const { versions } = this.state; const versionData = versions.filter( (v) => v.version.split(".").join("") === version )[0]; versionData.message = message; this.updateVersion(version, versionData); }; updateVersionActive = (active, version) => { this.setState({ refreshing: true }); const { versions } = this.state; const versionData = versions.filter( (v) => v.version.split(".").join("") === version )[0]; versionData.active = active; this.updateVersion(version, versionData); }; updateVersion = (version, versionData) => { postUpdateVersionActive({ version, versionData }) .then(() => this.refreshData()) .catch((err) => notification.error({ description: err.message, message: "Unable To Update", }) ); }; getSourceName(source) { return PLAYER_SOURCES.filter((p) => p.value === source)[0].text; } handleVideoSelectorChange = (e, s) => { const { selectors } = this.state; selectors[s] = e.target.value; this.setState({ selectors }); }; updateVideoSelector = () => { this.setState({ refreshing: true }); const { selectors } = this.state; postUpdateVideoSelectors({ selectors }) .then(() => this.refreshData()) .catch((err) => { notification.error({ description: err.message, message: "Unable To Update", }); this.setState({ refreshing: false }); }); }; handleVersionMessageChange = (e, v) => { const { versions } = this.state; versions.filter((vs) => vs.version === v)[0].message = e.target.value; this.setState({ versions }); }; handleAlertTitleChange = (value, version, index) => { const { versions } = this.state; versions.filter((vs) => vs.version === version)[0].alerts[ index ].title = value; this.setState({ versions }); }; handleAlertDescriptionChange = (value, version, index) => { const { versions } = this.state; versions.filter((vs) => vs.version === version)[0].alerts[ index ].description = value; this.setState({ versions }); }; handleAlertLinkChange = (value, version, index) => { const { versions } = this.state; versions.filter((vs) => vs.version === version)[0].alerts[ index ].link = value; this.setState({ versions }); }; handleAlertLinkTextChange = (value, version, index) => { const { versions } = this.state; versions.filter((vs) => vs.version === version)[0].alerts[ index ].linkText = value; this.setState({ versions }); }; deleteAlert = (version, index) => { const { versions } = this.state; versions.filter((vs) => vs.version === version)[0].alerts.splice(index, 1); if ( versions.filter((vs) => vs.version === version)[0].importantAlert === index || versions.filter((vs) => vs.version === version)[0].alerts.length < index ) { versions.filter( (vs) => vs.version === version )[0].importantAlert = undefined; } this.setState({ versions }); }; addEmptyAlert = (version) => { const { versions } = this.state; versions .filter((vs) => vs.version === version)[0] .alerts.push({ title: "", description: "", link: "", linkText: "", }); this.setState({ versions }); }; saveAlerts = (version) => { this.setState({ refreshing: true }); const { versions } = this.state; const versionData = versions.filter((v) => v.version === version)[0]; if (versionData.alerts.length > 0) this.updateVersion(version.split(".").join(""), versionData); else { notification.error({ message: "Missing Alerts", description: "Must have atleast one alert", }); this.setState({ refreshing: false }); } }; changeImportantAlert = (active, version, index) => { const { versions } = this.state; if (active === false) { versions.filter( (vs) => vs.version === version )[0].importantAlert = undefined; } else { versions.filter((vs) => vs.version === version)[0].importantAlert = index; } this.setState({ versions }); }; getAddMessageText = () => { const { activeTab } = this.state; switch (activeTab) { case "1": return "Send Message"; case "2": return "Add Version"; case "3": return "Add Video Selector"; case "4": return "Add Name Selector"; default: return ""; } }; getPrettyTime = (t) => { return t > 3600 ? new Date(t * 1000).toISOString().substr(11, 8) : new Date(t * 1000).toISOString().substr(14, 5); }; handleVideoNameSelectorChange = (event, source, type) => { const { nameSelectors } = this.state; nameSelectors[source][type] = event.target.value; this.setState({ nameSelectors }); }; updateVideoNameSelector = () => { this.setState({ refreshing: true }); const { nameSelectors } = this.state; postUpdateVideoNameSelectors({ nameSelectors }) .then(() => this.refreshData()) .catch((err) => { notification.error({ description: err.message, message: "Unable To Update", }); this.setState({ refreshing: false }); }); }; sendOrAdd = () => { const { activeTab } = this.state; switch (activeTab) { case "1": this.setState({ messageModel: true }); break; default: break; } }; editMessage = (value, field) => { const { newMessage } = this.state; newMessage[field] = value; this.setState({ newMessage }); }; sendMessage = async () => { this.setState({ refreshing: true }); const { selectedParties, newMessage } = this.state; newMessage.time = new Date().toLocaleString(); await postSendMessage({ selectedParties, newMessage }).catch((err) => { notification.error({ description: err.message, message: "Unable To Send Message", }); }); this.setState({ refreshing: false, newMessage: { type: "", text: "", uid: "", name: "", endpoint: "", }, messageModel: false, }); }; render() { const { partiesOverview, activeTab, refreshing, selectedParties, versions, nameSelectors, selectors, activeLimit, afkLimit, messageModel, newMessage, } = this.state; const partiesColumns = [ { title: "Party Name", dataIndex: "id", sorter: (a, b) => (a.id.toLowerCase() < b.id.toLowerCase() ? -1 : 1), }, { title: "Source", dataIndex: "source", sorter: (a, b) => a.source.toLowerCase() < b.source.toLowerCase() ? -1 : 1, filters: [ ...PLAYER_SOURCES, { text: "Is Playing", value: "PLAYING", }, { text: "Is Paused", value: "PAUSED", }, ], onFilter: (v, r) => r.source === v || r.status === v, render: (a, r) => ( ), }, { title: "Created At", dataIndex: "createTime", defaultSortOrder: "descend", sorter: (a, b) => Date.parse(a.createTime) - Date.parse(b.createTime), render: (a) => new Date(Date.parse(a)).toLocaleString(), }, { title: "Updated At", dataIndex: "updateTime", filters: [ { text: "Active", value: "a", }, { text: "Inactive", value: "i", }, ], onFilter: (v, r) => v === "a" ? (Date.now() - Date.parse(r.updateTime)) / 60000 < afkLimit : (Date.now() - Date.parse(r.updateTime)) / 60000 > afkLimit, sorter: (a, b) => Date.parse(a.updateTime) - Date.parse(b.updateTime), render: (a) => { const minutes = (Date.now() - Date.parse(a)) / 60000; const dS = new Date(a).toLocaleString(); return ( {dS} ); }, }, { title: "On Call", dataIndex: "onCall", filters: [ { text: "On Call", value: true, }, { text: "Not On Call", value: false, }, ], render: (a) => a ? ( ) : ( ), onFilter: (v, r) => r.onCall === v, }, ]; const versionsColumns = [ { title: "Version", dataIndex: "version", defaultSortOrder: "descend", sorter: (a, b) => parseInt(a.version.split(".").join("")) - parseInt(b.version.split(".").join("")), }, { title: "Active", dataIndex: "active", filters: [ { text: "Active", value: true, }, { text: "Inactive", value: false, }, ], onFilter: (v, r) => r.active === v, render: (a, r) => ( this.updateVersionActive(c, r.version.split(".").join("")) } checked={a} /> ), }, { title: "Inactive Message", dataIndex: "message", render: (a, r) => ( this.updateVersionMessage(v, r.version.split(".").join("")) } value={a} onChange={(e) => this.handleVersionMessageChange(e, r.version)} enterButton={} /> ), }, ]; return (
firebase.auth().signOut()} key="1" type="primary" danger icon={} > Logout , ]} /> this.setState({ messageModel: false }), }} okButtonProps={{ disabled: refreshing, icon: , onClick: this.sendMessage, }} okText="Send Message" > this.editMessage(e.target.value, "text")} placeholder="Message" /> {(newMessage.type === "interactive/link" || newMessage.type === "interactive/text") && ( this.editMessage(e.target.value, "endpoint")} placeholder={ newMessage.type === "interactive/link" ? "Link" : "Endpoint" } /> )} {newMessage.type === "interactive/link" && ( this.editMessage(e.target.value, "name")} placeholder="Link Text" /> )} this.setState({ activeLimit })} /> ), activeTab === "1" && ( this.setState({ afkLimit })} /> ), , activeTab === "3" && ( ), activeTab === "4" && ( ), ]} /> this.setState({ selectedParties }), }} loading={partiesOverview === null || refreshing} pagination={{ position: ["bottomCenter"], total: partiesOverview ? partiesOverview.length : 0, showTotal: (t, r) => `${r[0]} to ${r[1]} of ${t}`, defaultPageSize: 10, showSizeChanger: true, pageSizeOptions: [10, 25, 50], }} rowKey={(p) => p.id} dataSource={partiesOverview} columns={partiesColumns} expandable={{ rowExpandable: (r) => true, expandedRowClassName: () => "party-details-wrapper", expandedRowRender: (r) => ( {r.userCount} {r.messageCount} {r.showName} {this.getPrettyTime(r.time)} ), }} />
p.version} dataSource={versions} columns={versionsColumns} expandable={{ rowExpandable: (r) => r.alerts, expandedRowClassName: () => "alerts-wrapper", expandedRowRender: (r) => ( {r.alerts.map((a, i) => ( this.deleteAlert(r.version, i)} danger icon={} shape="circle" color="danger" />, this.changeImportantAlert( active, r.version, i ) } checked={ r.importantAlert && r.importantAlert === i } />, ]} > this.handleAlertTitleChange( e.target.value, r.version, i ) } /> this.handleAlertDescriptionChange( e.target.value, r.version, i ) } /> Link this.handleAlertLinkTextChange( e.target.value, r.version, i ) } /> this.handleAlertLinkChange( e.target.value, r.version, i ) } /> ))} this.addEmptyAlert(r.version)} className="add-alert-card" > this.saveAlerts(r.version)} className="add-alert-card" > ), }} /> {Object.keys(selectors) .sort() .map((source) => ( this.handleVideoSelectorChange(e, source)} /> ))} {Object.keys(nameSelectors) .sort() .map((source) => ( this.handleVideoNameSelectorChange(e, source, "show") } /> this.handleVideoNameSelectorChange( e, source, "episode" ) } /> ))} ); } }