general changes and developing
submit migration form upcoming migration table and formatting
This commit is contained in:
parent
fa4ac953ce
commit
9a6524d7f9
47
package-lock.json
generated
47
package-lock.json
generated
@ -7768,6 +7768,32 @@
|
|||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"formik": {
|
||||||
|
"version": "2.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/formik/-/formik-2.2.5.tgz",
|
||||||
|
"integrity": "sha512-KkOsyYmh5xsow+wlbdL9QSkqvbiHSb1RIToBKiooCFW4lyypn+ZlHGjTuuOqUWBqZaI5nCEupeI275Mo6tFBzg==",
|
||||||
|
"requires": {
|
||||||
|
"deepmerge": "^2.1.1",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"lodash": "^4.17.14",
|
||||||
|
"lodash-es": "^4.17.14",
|
||||||
|
"react-fast-compare": "^2.0.1",
|
||||||
|
"tiny-warning": "^1.0.2",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"deepmerge": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formstrap": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/formstrap/-/formstrap-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-7eI/Qt7NTlIl1I0tyXFZPZ98ngC+97MMYd4yejpTt/sJkTRDQMsKqxWev4kEUR6sTEA2Lyoy+2TWQ8FqAwCIgg=="
|
||||||
|
},
|
||||||
"forwarded": {
|
"forwarded": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
@ -11308,6 +11334,11 @@
|
|||||||
"sass-loader": "^8.0.2"
|
"sass-loader": "^8.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"moment": {
|
||||||
|
"version": "2.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||||
|
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||||
|
},
|
||||||
"react": {
|
"react": {
|
||||||
"version": "16.14.0",
|
"version": "16.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
|
||||||
@ -11667,9 +11698,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.24.0",
|
"version": "2.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||||
},
|
},
|
||||||
"move-concurrently": {
|
"move-concurrently": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -14091,6 +14122,11 @@
|
|||||||
"underscore": "1.9.1"
|
"underscore": "1.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-bootstrap-table2-filter": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-bootstrap-table2-filter/-/react-bootstrap-table2-filter-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-lE+CHHGewzN9PCPaRqbu9wia8NMfwOBMPOAoAyfxbbMrZzjCf1WYRrHbWGzUj1MQlF5kJxLMwRgy/C604OmgMw=="
|
||||||
|
},
|
||||||
"react-bootstrap-table2-paginator": {
|
"react-bootstrap-table2-paginator": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-bootstrap-table2-paginator/-/react-bootstrap-table2-paginator-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-bootstrap-table2-paginator/-/react-bootstrap-table2-paginator-2.1.2.tgz",
|
||||||
@ -14293,6 +14329,11 @@
|
|||||||
"warning": "^4.0.1"
|
"warning": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-fast-compare": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||||
|
},
|
||||||
"react-fit": {
|
"react-fit": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-fit/-/react-fit-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-fit/-/react-fit-1.3.1.tgz",
|
||||||
|
|||||||
@ -12,14 +12,18 @@
|
|||||||
"axios-react": "^2.0.2",
|
"axios-react": "^2.0.2",
|
||||||
"bootstrap": "^4.5.3",
|
"bootstrap": "^4.5.3",
|
||||||
"cldr-data": "^36.0.0",
|
"cldr-data": "^36.0.0",
|
||||||
|
"formik": "^2.2.5",
|
||||||
|
"formstrap": "^1.1.3",
|
||||||
"globalize": "^1.6.0",
|
"globalize": "^1.6.0",
|
||||||
"iana-tz-data": "^2019.1.0",
|
"iana-tz-data": "^2019.1.0",
|
||||||
"mdbreact": "^4.27.0",
|
"mdbreact": "^4.27.0",
|
||||||
|
"moment": "^2.29.1",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-bootstrap": "^1.4.0",
|
"react-bootstrap": "^1.4.0",
|
||||||
"react-bootstrap-form": "^0.1.4-beta6",
|
"react-bootstrap-form": "^0.1.4-beta6",
|
||||||
"react-bootstrap-table": "^4.3.1",
|
"react-bootstrap-table": "^4.3.1",
|
||||||
"react-bootstrap-table-next": "^4.0.3",
|
"react-bootstrap-table-next": "^4.0.3",
|
||||||
|
"react-bootstrap-table2-filter": "^1.3.3",
|
||||||
"react-bootstrap-table2-paginator": "^2.1.2",
|
"react-bootstrap-table2-paginator": "^2.1.2",
|
||||||
"react-bootstrap-table2-toolkit": "^2.1.3",
|
"react-bootstrap-table2-toolkit": "^2.1.3",
|
||||||
"react-data-grid": "^7.0.0-canary.28",
|
"react-data-grid": "^7.0.0-canary.28",
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon"
|
||||||
|
href="https://ventraip.com.au/wp-content/themes/ventraip-template/module/frontend/assets/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta name="description" content="Web site created using create-react-app" />
|
<meta name="description" content="Web site created using create-react-app" />
|
||||||
@ -12,9 +13,10 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://npmcdn.com/react-bootstrap-table/dist/react-bootstrap-table-all.min.css"></link>
|
<link rel="stylesheet" href="https://npmcdn.com/react-bootstrap-table/dist/react-bootstrap-table-all.min.css">
|
||||||
|
</link>
|
||||||
|
|
||||||
<title>React App</title>
|
<title>Migrations Tracker</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios'
|
import axios from "axios";
|
||||||
|
|
||||||
export const callAPI = axios.create({
|
export const callAPI = axios.create({
|
||||||
baseURL: 'https://devapi.benjamyn.love/migrations/'
|
baseURL: "https://devapi.benjamyn.love/migrations/",
|
||||||
})
|
});
|
||||||
|
|||||||
170
src/components/root/Pages/Forms/CPanelBooking.js
Normal file
170
src/components/root/Pages/Forms/CPanelBooking.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import "bootstrap/dist/css/bootstrap.min.css";
|
||||||
|
import moment from "moment";
|
||||||
|
import { Col, FormGroup, Row, Container, Label } from "reactstrap";
|
||||||
|
import { Formik, Form } from "formik";
|
||||||
|
import { Input, Submit } from "formstrap";
|
||||||
|
import { callAPI } from "../../../actions/API";
|
||||||
|
|
||||||
|
export const CPanelBooking = () => {
|
||||||
|
const initialValues = {
|
||||||
|
submit_time: moment().format("YYYY-MM-DD"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = async (values, { setSubmitting }) => {
|
||||||
|
console.log(values);
|
||||||
|
callAPI
|
||||||
|
.post("/")
|
||||||
|
.then(function (response) {
|
||||||
|
console.log(JSON.stringify(response.values));
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Formik initialValues={initialValues} onSubmit={onSubmit}>
|
||||||
|
<Form>
|
||||||
|
<Container>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedDate">Date</Label>
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
name="booked_date"
|
||||||
|
id="exampleDate"
|
||||||
|
placeholder="date placeholder"
|
||||||
|
mindate={Date()}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedTime">Timeslot</Label>
|
||||||
|
<Input type="select" name="booked_time" id="bookedTime">
|
||||||
|
<option>00:00-09:00</option>
|
||||||
|
<option>09:00-12:00</option>
|
||||||
|
<option>12:00-18:00</option>
|
||||||
|
<option>18:00-00:00</option>
|
||||||
|
</Input>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedDomain">Domain</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="domain"
|
||||||
|
id="bookedDomain"
|
||||||
|
placeholder="example.com.au"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedUsername">cPanel username</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
id="bookedUsername"
|
||||||
|
placeholder="example"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedSource">Original server</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="original_server"
|
||||||
|
id="bookedSource"
|
||||||
|
placeholder="1.2.3.4"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedDestination">New server</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="new_server"
|
||||||
|
id="bookedDestination"
|
||||||
|
placeholder="1.2.3.4"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedBrand">Brand</Label>
|
||||||
|
<Input type="select" name="brand" id="bookedBrand">
|
||||||
|
<option>VentraIP</option>
|
||||||
|
<option>Zuver</option>
|
||||||
|
<option>Synergy</option>
|
||||||
|
</Input>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedTicket">Ticket ID</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="ticket_id"
|
||||||
|
id="bookedTicket"
|
||||||
|
placeholder="VIP-A1234567"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedType">Migration type</Label>
|
||||||
|
<Input type="select" name="migration_type" id="bookedType">
|
||||||
|
<option>cPanel</option>
|
||||||
|
<option>Plesk</option>
|
||||||
|
<option>Other</option>
|
||||||
|
</Input>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedAgent">Agent Initials</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="agent_booked"
|
||||||
|
id="bookedAgent"
|
||||||
|
placeholder="SZ"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedAdditionalDomains">Addon Domains</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="additional_domains"
|
||||||
|
id="bookedAdditionalDomains"
|
||||||
|
placeholder="example.com.au,example.net.au"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedTermDate">
|
||||||
|
Estimated termination date (internal migrations only)
|
||||||
|
</Label>
|
||||||
|
<Input type="date" name="term_date" id="bookedTermDate" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Label for="bookedNotes">Notes</Label>
|
||||||
|
<Input type="textarea" name="notes" id="notes" />
|
||||||
|
</FormGroup>
|
||||||
|
<Submit withSpinner>Save</Submit>
|
||||||
|
</Container>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
};
|
||||||
39
src/components/root/Pages/Forms/DateTimePicker.js
Normal file
39
src/components/root/Pages/Forms/DateTimePicker.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from "react";
|
||||||
|
var DatePicker = require("reactstrap-date-picker");
|
||||||
|
|
||||||
|
class DateTimePicker extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
value: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(value, formattedValue) {
|
||||||
|
this.setState({
|
||||||
|
value: value, // ISO String, ex: "2016-11-19T12:00:00.000Z"
|
||||||
|
formattedValue: formattedValue, // Formatted String, ex: "11/19/2016"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
// Access ISO String and formatted values from the DOM.
|
||||||
|
var hiddenInputElement = document.getElementById("example-datepicker");
|
||||||
|
console.log(hiddenInputElement.value); // ISO String, ex: "2016-11-19T12:00:00.000Z"
|
||||||
|
console.log(hiddenInputElement.getAttribute("data-formattedvalue")); // Formatted String, ex: "11/19/2016"
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<DatePicker
|
||||||
|
id="example-datepicker"
|
||||||
|
value={this.state.value}
|
||||||
|
onChange={(v, f) => this.handleChange(v, f)}
|
||||||
|
minDate={Date()}
|
||||||
|
width="100%"
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default DateTimePicker;
|
||||||
11
src/components/root/Pages/Forms/EmailBooking.js
Normal file
11
src/components/root/Pages/Forms/EmailBooking.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
|
||||||
|
export default class EmailBooking extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Email Migration</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,158 +1,66 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Container,
|
TabContent,
|
||||||
Col,
|
TabPane,
|
||||||
Form,
|
Nav,
|
||||||
FormGroup,
|
NavItem,
|
||||||
Label,
|
NavLink,
|
||||||
Input,
|
|
||||||
FormText,
|
|
||||||
Button,
|
|
||||||
Row,
|
Row,
|
||||||
|
Col,
|
||||||
} from "reactstrap";
|
} from "reactstrap";
|
||||||
import PropTypes from "prop-types";
|
import classnames from "classnames";
|
||||||
import "bootstrap/dist/css/bootstrap.min.css";
|
|
||||||
|
import { CPanelBooking } from "./CPanelBooking";
|
||||||
|
import EmailBooking from "./EmailBooking";
|
||||||
|
|
||||||
|
const FormPage = (props) => {
|
||||||
|
const [activeTab, setActiveTab] = useState("1");
|
||||||
|
|
||||||
|
const toggle = (tab) => {
|
||||||
|
if (activeTab !== tab) setActiveTab(tab);
|
||||||
|
};
|
||||||
|
|
||||||
const FormPage = () => {
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<div>
|
||||||
<Container>
|
<Nav tabs>
|
||||||
|
<NavItem>
|
||||||
|
<NavLink
|
||||||
|
className={classnames({ active: activeTab === "1" })}
|
||||||
|
onClick={() => {
|
||||||
|
toggle("1");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Web hosting Migration
|
||||||
|
</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
<NavItem>
|
||||||
|
<NavLink
|
||||||
|
className={classnames({ active: activeTab === "2" })}
|
||||||
|
onClick={() => {
|
||||||
|
toggle("2");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Email Migration
|
||||||
|
</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
</Nav>
|
||||||
|
<TabContent activeTab={activeTab}>
|
||||||
|
<TabPane tabId="1">
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col sm="12">
|
||||||
<FormGroup>
|
<CPanelBooking />
|
||||||
<Label for="bookedDate">Date</Label>
|
|
||||||
<Input type="date" name="date" id="bookedDate" />
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedTime">Timeslot</Label>
|
|
||||||
<Input type="select" name="select" id="bookedTime">
|
|
||||||
<option>00:00-09:00</option>
|
|
||||||
<option>09:00-12:00</option>
|
|
||||||
<option>12:00-18:00</option>
|
|
||||||
<option>18:00-00:00</option>
|
|
||||||
</Input>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedDomain">Domain</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
name="text"
|
|
||||||
id="bookedDomain"
|
|
||||||
placeholder="example.com.au"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedUsername">cPanel username</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
name="text"
|
|
||||||
id="bookedUsername"
|
|
||||||
placeholder="example"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tabId="2">
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col sm="6">
|
||||||
<FormGroup>
|
<EmailBooking />
|
||||||
<Label for="bookedSource">Original server</Label>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
name="number"
|
|
||||||
id="bookedSource"
|
|
||||||
placeholder="1.2.3.4"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedDestination">New server</Label>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
name="number"
|
|
||||||
id="bookedDestination"
|
|
||||||
placeholder="1.2.3.4"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
</TabPane>
|
||||||
<Col>
|
</TabContent>
|
||||||
<FormGroup>
|
</div>
|
||||||
<Label for="bookedBrand">Brand</Label>
|
|
||||||
<Input type="select" name="select" id="bookedBrand">
|
|
||||||
<option>VentraIP</option>
|
|
||||||
<option>Zuver</option>
|
|
||||||
<option>Synergy</option>
|
|
||||||
</Input>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedTicket">Ticket ID</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
name="text"
|
|
||||||
id="bookedTicket"
|
|
||||||
placeholder="VIP-A1234567"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedType">Migration type</Label>
|
|
||||||
<Input type="select" name="select" id="bookedType">
|
|
||||||
<option>cPanel</option>
|
|
||||||
<option>Plesk</option>
|
|
||||||
<option>Other</option>
|
|
||||||
</Input>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedAgent">Agent Initials</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
name="text"
|
|
||||||
id="bookedAgent"
|
|
||||||
placeholder="SZ"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedAdditionalDomains">Addon Domains</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
name="text"
|
|
||||||
id="bookedAdditionalDomains"
|
|
||||||
placeholder="example.com.au,example.net.au"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedTermDate">
|
|
||||||
Estimated termination date (internal migrations only)
|
|
||||||
</Label>
|
|
||||||
<Input type="date" name="date" id="bookedTermDate" />
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="bookedNotes">Notes</Label>
|
|
||||||
<Input type="textarea" name="text" id="notes" />
|
|
||||||
</FormGroup>
|
|
||||||
<Button>Submit</Button>
|
|
||||||
</Container>
|
|
||||||
</Form>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default FormPage;
|
export default FormPage;
|
||||||
|
|
||||||
{
|
|
||||||
/* <Button>Submit</Button> */
|
|
||||||
}
|
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function SubmitMigration() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
135
src/components/root/Pages/Migrations/UpcomingSingle copy.old
Normal file
135
src/components/root/Pages/Migrations/UpcomingSingle copy.old
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { BootstrapTable, TableHeaderColumn } from "react-bootstrap-table";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
import "../../../../../node_modules/react-bootstrap-table/dist/react-bootstrap-table-all.min.css"; // I love this
|
||||||
|
|
||||||
|
export const UpcomingSingle = ({ data }) => {
|
||||||
|
const options = {
|
||||||
|
sizePerPageList: [
|
||||||
|
{
|
||||||
|
text: "100",
|
||||||
|
value: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "200",
|
||||||
|
value: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "All",
|
||||||
|
value: data.length,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sizePerPage: 30,
|
||||||
|
// pageStartIndex: 0,
|
||||||
|
// prePage: "Prev",
|
||||||
|
// nextPage: "Next",
|
||||||
|
formatter: (cell, row) => <a href={cell}> {cell} </a>,
|
||||||
|
};
|
||||||
|
console.log(data.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<BootstrapTable
|
||||||
|
data={data}
|
||||||
|
height="100%"
|
||||||
|
striped
|
||||||
|
hover
|
||||||
|
condensed
|
||||||
|
pagination
|
||||||
|
version="4"
|
||||||
|
options={options}
|
||||||
|
>
|
||||||
|
<TableHeaderColumn
|
||||||
|
isKey
|
||||||
|
dataField="submit_time"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Submit time
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="domain"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Domain
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="booked_time"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Booked time
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="original_server"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Original server
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="new_server"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
New server
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="username"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Username
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="brand"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Brand
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="ticket_id"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Ticket ID
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="migration_status"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="agent_booked"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Agent
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="additional_domains"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Additional Domains
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="migration_type"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Migration type
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="term_date"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
est. Terminaton Date
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn
|
||||||
|
dataField="migration_cmd"
|
||||||
|
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
||||||
|
>
|
||||||
|
Migration CMD
|
||||||
|
</TableHeaderColumn>
|
||||||
|
<TableHeaderColumn dataField="notes">Notes</TableHeaderColumn>
|
||||||
|
</BootstrapTable>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
//
|
||||||
|
};
|
||||||
|
{
|
||||||
|
}
|
||||||
@ -1,131 +1,145 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { BootstrapTable, TableHeaderColumn } from "react-bootstrap-table";
|
import { callAPI } from "../../../actions/API";
|
||||||
import "../../../../../node_modules/react-bootstrap-table/dist/react-bootstrap-table-all.min.css";
|
import BootstrapTable from "react-bootstrap-table-next";
|
||||||
|
import paginationFactory from "react-bootstrap-table2-paginator";
|
||||||
|
import * as ReactBootstrap from "react-bootstrap";
|
||||||
|
import filterFactory from "react-bootstrap-table2-filter";
|
||||||
|
import ToolkitProvider, {
|
||||||
|
Search,
|
||||||
|
CSVExport,
|
||||||
|
} from "react-bootstrap-table2-toolkit";
|
||||||
|
|
||||||
export const UpcomingSingle = ({ data }) => {
|
const UpcomingSingle = () => {
|
||||||
|
const [list, setList] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const { SearchBar } = Search;
|
||||||
|
const { ExportCSVButton } = CSVExport;
|
||||||
|
const sizePerPageRenderer = ({
|
||||||
|
options,
|
||||||
|
currSizePerPage,
|
||||||
|
onSizePerPageChange,
|
||||||
|
}) => (
|
||||||
|
<div className="btn-group" role="group">
|
||||||
|
{options.map((option) => {
|
||||||
|
const isSelect = currSizePerPage === `${option.page}`;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={option.text}
|
||||||
|
type="button"
|
||||||
|
onClick={() => onSizePerPageChange(option.page)}
|
||||||
|
className={`btn ${isSelect ? "btn-secondary" : "btn-success"}`}
|
||||||
|
>
|
||||||
|
{option.text}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const getListData = async () => {
|
||||||
|
try {
|
||||||
|
const data = await callAPI.get("/all/");
|
||||||
|
console.log(data);
|
||||||
|
setList(data.data);
|
||||||
|
setLoading(true);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
const options = {
|
const options = {
|
||||||
|
paginationSize: 4,
|
||||||
|
pageStartIndex: 0,
|
||||||
|
// alwaysShowAllBtns: true, // Always show next and previous button
|
||||||
|
// withFirstAndLast: false, // Hide the going to First and Last page button
|
||||||
|
// hideSizePerPage: true, // Hide the sizePerPage dropdown always
|
||||||
|
// hidePageListOnlyOnePage: true, // Hide the pagination list when only one page
|
||||||
|
firstPageText: "First",
|
||||||
|
prePageText: "Back",
|
||||||
|
nextPageText: "Next",
|
||||||
|
lastPageText: "Last",
|
||||||
|
nextPageTitle: "First page",
|
||||||
|
prePageTitle: "Pre page",
|
||||||
|
firstPageTitle: "Next page",
|
||||||
|
lastPageTitle: "Last page",
|
||||||
|
showTotal: true,
|
||||||
|
disablePageTitle: true,
|
||||||
sizePerPageList: [
|
sizePerPageList: [
|
||||||
|
{
|
||||||
|
text: "50",
|
||||||
|
value: 50,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: "100",
|
text: "100",
|
||||||
value: 100,
|
value: 100,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: "200",
|
|
||||||
value: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "All",
|
|
||||||
value: data.length,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
sizePerPage: 20,
|
sizePerPageRenderer, // A numeric array is also available. the purpose of above example is custom the text
|
||||||
// pageStartIndex: 0,
|
|
||||||
// prePage: "Prev",
|
|
||||||
// nextPage: "Next",
|
|
||||||
};
|
};
|
||||||
|
const columns = [
|
||||||
|
{ dataField: "id", text: "ID", hidden: true },
|
||||||
|
{ dataField: "submit_time", text: "Submit Time", sort: true },
|
||||||
|
{ dataField: "domain", text: "Domain", sort: true },
|
||||||
|
{ dataField: "booked_time", text: "Booked Time", sort: true },
|
||||||
|
{ dataField: "original_server", text: "Original Server", sort: true },
|
||||||
|
{ dataField: "new_server", text: "New Server", sort: true },
|
||||||
|
{ dataField: "username", text: "Username", sort: true },
|
||||||
|
{ dataField: "brand", text: "Brand", sort: true },
|
||||||
|
{ dataField: "ticket_id", text: "TicketID", sort: true },
|
||||||
|
{ dataField: "migration_status", text: "Status", sort: true },
|
||||||
|
{ dataField: "agent_booked", text: "Agent initials", sort: true },
|
||||||
|
{ dataField: "additional_domains", text: "Additional Domains", sort: true },
|
||||||
|
{ dataField: "migration_type", text: "Type", sort: true },
|
||||||
|
{ dataField: "term_date", text: "Termination Date", sort: true },
|
||||||
|
{ dataField: "notes", text: "Notes", sort: true },
|
||||||
|
{
|
||||||
|
dataField: "report",
|
||||||
|
text: "Show Detailed Report",
|
||||||
|
formatter: (cell, row) => <a href={cell + row.id}> {cell} </a>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getListData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="UpcomingSingle">
|
||||||
|
{loading ? (
|
||||||
|
<ToolkitProvider
|
||||||
|
keyField="name"
|
||||||
|
data={list}
|
||||||
|
columns={columns}
|
||||||
|
search
|
||||||
|
exportCSV
|
||||||
|
>
|
||||||
|
{(props) => (
|
||||||
<div>
|
<div>
|
||||||
|
<div className="serSec">
|
||||||
|
<h3 className="hdrOne"></h3>
|
||||||
|
<SearchBar {...props.searchProps} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
<BootstrapTable
|
<BootstrapTable
|
||||||
data={data}
|
{...props.baseProps}
|
||||||
height="100%"
|
filter={filterFactory()}
|
||||||
|
pagination={paginationFactory(options)}
|
||||||
striped
|
striped
|
||||||
hover
|
hover
|
||||||
condensed
|
condensed
|
||||||
pagination
|
/>
|
||||||
version="4"
|
</div>
|
||||||
options={options}
|
<ExportCSVButton {...props.csvProps}>
|
||||||
>
|
Export CSV!!
|
||||||
<TableHeaderColumn
|
</ExportCSVButton>
|
||||||
isKey
|
</div>
|
||||||
dataField="submit_time"
|
)}
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
</ToolkitProvider>
|
||||||
>
|
) : (
|
||||||
Submit time
|
<ReactBootstrap.Spinner animation="border" />
|
||||||
</TableHeaderColumn>
|
)}
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="domain"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Domain
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="booked_time"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Booked time
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="original_server"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Original server
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="new_server"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
New server
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="username"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Username
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn dataField="notes">Notes</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="brand"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Brand
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="ticket_id"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Ticket ID
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="migration_status"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="agent_booked"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Agent
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="additional_domains"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Additional Domains
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="migration_type"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Migration type
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="term_date"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
est. Terminaton Date
|
|
||||||
</TableHeaderColumn>
|
|
||||||
<TableHeaderColumn
|
|
||||||
dataField="migration_cmd"
|
|
||||||
filter={{ type: "TextFilter", delay: 300, placeholder: "Filter" }}
|
|
||||||
>
|
|
||||||
Migration CMD
|
|
||||||
</TableHeaderColumn>
|
|
||||||
</BootstrapTable>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
//
|
|
||||||
};
|
};
|
||||||
{
|
|
||||||
}
|
export default UpcomingSingle;
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
|
||||||
import { callAPI } from "../../actions/API";
|
import UpcomingSingle from "./Migrations/UpcomingSingle";
|
||||||
import { UpcomingSingle } from "./Migrations/UpcomingSingle";
|
|
||||||
import Error from "../../actions/Error";
|
|
||||||
|
|
||||||
class Upcoming extends Component {
|
class Upcoming extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -12,30 +10,6 @@ class Upcoming extends Component {
|
|||||||
e: false,
|
e: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
|
||||||
callAPI
|
|
||||||
.get("all/")
|
|
||||||
.then((request) => {
|
|
||||||
this.setState({
|
|
||||||
sidemigs: request.data,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
this.setState({
|
|
||||||
error: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderItems() {
|
|
||||||
if (!this.state.e) {
|
|
||||||
return this.state.sidemigs.map((data) => (
|
|
||||||
<UpcomingSingle key={data.id} data={this.state.sidemigs} />
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
return <Error />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user