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.' }, { text: 'Apple TV+', value: 'tv.apple.' }, { text: 'Viki', value: 'viki.' }, { text: 'Funimation', value: 'funimation.' }, { text: 'Rooster Teeth', value: 'roosterteeth.' }, { text: 'VRV', value: 'vrv.' }, { text: 'Stan', value: 'stan.' }, { text: 'Hotstar', value: 'hotstar.' }, { text: 'Airtel', value: 'airtelxstream.' }, { text: 'Vimeo', value: 'vimeo.' }, { text: 'Dailymotion', value: 'dailymotion.' }, ]; 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')} /> ))} ); } }