diff --git a/src/main/frontend/src/components/AddCatalogPriceForm.js b/src/main/frontend/src/components/AddCatalogPriceForm.js
index dfb9d92065..3269bf07e3 100644
--- a/src/main/frontend/src/components/AddCatalogPriceForm.js
+++ b/src/main/frontend/src/components/AddCatalogPriceForm.js
@@ -2,12 +2,162 @@
// IMPORTANT:
// You must update ResourceUrl.RESOURCES_VERSION each time whenever you're modified this file!
//
+// @todo #1342 AddCatalogPriceForm: show a currency
class AddCatalogPriceForm extends React.Component {
-
+ constructor(props) {
+ super(props);
+ this.state = {
+ price: null,
+ catalog: 'michel',
+ hasServerError: false,
+ validationErrors: [],
+ isDisabled: false
+ };
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleChangePrice = this.handleChangePrice.bind(this);
+ this.handleChangeCatalog = this.handleChangeCatalog.bind(this);
+ }
+
+ handleChangePrice(event) {
+ event.preventDefault();
+ this.setState({
+ price: event.target.value
+ });
+ }
+
+ handleChangeCatalog(event) {
+ event.preventDefault();
+ this.setState({
+ catalog: event.target.value
+ });
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+
+ this.setState({
+ isDisabled: true,
+ hasServerError: false,
+ validationErrors: []
+ });
+
+ axios.patch(
+ this.props.url,
+ [
+ {
+ op: 'add',
+ path: `/${this.state.catalog}_price`,
+ value: this.state.price
+ }
+ ],
+ {
+ headers: {
+ [this.props.csrfHeaderName]: this.props.csrfTokenValue,
+ 'Cache-Control': 'no-store'
+ },
+ validateStatus: status => {
+ return status == 204 || status == 400;
+ }
+ }
+ )
+ .then(response => {
+ const data = response.data;
+ if (data.hasOwnProperty('fieldErrors')) {
+ const fieldErrors = [];
+ if (data.fieldErrors.price) {
+ fieldErrors.push(...data.fieldErrors.price);
+ }
+
+ this.setState({
+ isDisabled: false,
+ validationErrors: fieldErrors
+ });
+ return;
+ }
+
+ // no need to reset the state as page will be reloaded
+ window.location.reload();
+ })
+ .catch(error => {
+ console.error(error);
+ this.setState({ isDisabled: false, hasServerError: true });
+ });
+ }
render() {
+ const hasValidationErrors = this.state.validationErrors.length > 0;
return (
-
- )
+
+ );
}
}
diff --git a/src/main/frontend/src/components/AddCommentForm.js b/src/main/frontend/src/components/AddCommentForm.js
index ed94008e32..1671fd2da6 100644
--- a/src/main/frontend/src/components/AddCommentForm.js
+++ b/src/main/frontend/src/components/AddCommentForm.js
@@ -4,10 +4,114 @@
//
class AddCommentForm extends React.Component {
-
+ constructor(props) {
+ super(props);
+ this.state = {
+ comment: '',
+ validationErrors: [],
+ hasServerError: false,
+ isDisabled: false
+ };
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+
+ handleChange(event) {
+ event.preventDefault();
+ this.setState({
+ comment: event.target.value
+ });
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+
+ this.setState({
+ isDisabled: true,
+ hasServerError: false,
+ validationErrors: []
+ });
+
+ axios.patch(
+ this.props.url, [
+ {
+ op: 'add',
+ path: '/comment',
+ value: this.state.comment
+ }
+ ],
+ {
+ headers: {
+ [this.props.csrfHeaderName]: this.props.csrfTokenValue,
+ 'Cache-Control': 'no-store'
+ },
+ validateStatus: status => {
+ return status == 204 || status == 400;
+ }
+ })
+ .then(response => {
+ const data = response.data;
+
+ if (data.hasOwnProperty('fieldErrors')) {
+ const fieldErrors = [];
+ if (data.fieldErrors.comment) {
+ fieldErrors.push(...data.fieldErrors.comment);
+ }
+ this.setState({
+ isDisabled: false,
+ validationErrors: fieldErrors
+ });
+ return;
+ }
+
+ // no need to reset the state as page will be reloaded
+ window.location.reload();
+ })
+ .catch(error => {
+ console.error(error);
+ this.setState({ isDisabled: false, hasServerError: true });
+ });
+ }
render() {
+ const hasValidationErrors = this.state.validationErrors.length > 0;
return (
-
- )
+
+ );
}
}
diff --git a/src/main/frontend/src/components/AddReleaseYearForm.js b/src/main/frontend/src/components/AddReleaseYearForm.js
index bd83d58bc5..dfc238a05e 100644
--- a/src/main/frontend/src/components/AddReleaseYearForm.js
+++ b/src/main/frontend/src/components/AddReleaseYearForm.js
@@ -4,10 +4,131 @@
//
class AddReleaseYearForm extends React.Component {
-
+ constructor(props) {
+ super(props);
+ this.state = {
+ year: null,
+ hasServerError: false,
+ validationErrors: [],
+ isDisabled: false,
+ };
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+
+ generateRange(start, end) {
+ return new Array(end - start + 1)
+ .fill()
+ .map((_, idx) => start + idx);
+ }
+
+ handleChange(event) {
+ event.preventDefault();
+ this.setState({
+ year: event.target.value
+ });
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+
+ this.setState({
+ isDisabled: true,
+ hasServerError: false,
+ validationErrors: []
+ });
+
+ axios.patch(
+ this.props.url,
+ [
+ {
+ op: 'add',
+ path: '/release_year',
+ value: this.state.year
+ }
+ ],
+ {
+ headers: {
+ [this.props.csrfHeaderName]: this.props.csrfTokenValue,
+ 'Cache-Control': 'no-store'
+ },
+ validateStatus: status => {
+ return status == 204 || status == 400;
+ }
+ }
+ )
+ .then(response => {
+ const data = response.data;
+ if (data.hasOwnProperty('fieldErrors')) {
+ const fieldErrors = [];
+ if (data.fieldErrors.year) {
+ fieldErrors.push(...data.fieldErrors.year);
+ }
+ this.setState({
+ isDisabled: false,
+ validationErrors: fieldErrors
+ });
+ return;
+ }
+
+ // no need to reset the state as page will be reloaded
+ window.location.reload();
+ })
+ .catch(error => {
+ console.error(error);
+ this.setState({ isDisabled: false, hasServerError: true });
+ });
+ }
+
render() {
+ const rangeOfYears = this.generateRange(
+ this.props.sinceYear,
+ this.props.tillYear
+ );
+ const hasValidationErrors = this.state.validationErrors.length > 0;
return (
-
- )
+
+ );
}
}
diff --git a/src/main/java/ru/mystamps/web/feature/site/ResourceUrl.java b/src/main/java/ru/mystamps/web/feature/site/ResourceUrl.java
index f84db39dc6..483fb0505e 100644
--- a/src/main/java/ru/mystamps/web/feature/site/ResourceUrl.java
+++ b/src/main/java/ru/mystamps/web/feature/site/ResourceUrl.java
@@ -32,7 +32,7 @@ public final class ResourceUrl {
public static final String STATIC_RESOURCES_URL = "https://stamps.filezz.ru";
// MUST be updated when any of our resources were modified
- public static final String RESOURCES_VERSION = "v0.4.3.1";
+ public static final String RESOURCES_VERSION = "v0.4.3.2";
// CheckStyle: ignore LineLength for next 14 lines
private static final String CATALOG_UTILS_JS = "/public/js/" + RESOURCES_VERSION + "/CatalogUtils.min.js";
diff --git a/src/main/webapp/WEB-INF/views/series/info.html b/src/main/webapp/WEB-INF/views/series/info.html
index 1b48373903..30296d257e 100644
--- a/src/main/webapp/WEB-INF/views/series/info.html
+++ b/src/main/webapp/WEB-INF/views/series/info.html
@@ -894,6 +894,51 @@ Add info about selling/buying thi
'altCurrency' : 'CZK'
}
}
+ }
+ };
+ var stubResponse;
+
+ switch (outcome) {
+ case 'success':
+ case 'failOnField':
+ stubResponse = possibleResponses[url][outcome];
+ break;
+ case 'failOnForm':
+ default:
+ stubResponse = {
+ status: 500,
+ statusText: 'Fake Server Error'
+ };
+ }
+
+ responseCount++;
+
+ return new Promise(function delayExecution(resolve) {
+ setTimeout(resolve, 500 /* 0.5 second */);
+
+ }).then(function returnResponse() {
+ return stubResponse.status == 500 ? Promise.reject(stubResponse) : Promise.resolve(stubResponse);
+ });
+ },
+ patch: function (url) {
+ var possibleOutcomes = ['failOnForm', 'failOnField', 'success'];
+ var outcome = possibleOutcomes[responseCount % possibleOutcomes.length];
+ var possibleResponses = {
+ '/series/100': {
+ 'failOnField': {
+ status: 400,
+ data: {
+ 'fieldErrors': {
+ 'comment': ['Comment error'],
+ 'year': ['Year error'],
+ 'price': ['Price error']
+ }
+ }
+ },
+ 'success': {
+ status: 200,
+ data: {}
+ }
}
};
var stubResponse;
@@ -937,8 +982,9 @@ Add info about selling/buying thi
var addReleaseYearProps = {
'csrfHeaderName': [[ ${_csrf.headerName} ]],
'csrfTokenValue': [[ ${_csrf.token} ]],
- 'l10n': {
- }
+ 'l10n': {},
+ 'sinceYear': 1840,
+ 'tillYear': new Date().getFullYear()
};
var addCatalogPriceProps = {
'csrfHeaderName': [[ ${_csrf.headerName} ]],
@@ -951,7 +997,9 @@ Add info about selling/buying thi
/*[- */
var addReleaseYearProps = {
'url': '/series/100',
- 'l10n': {}
+ 'l10n': {},
+ 'sinceYear': 1840,
+ 'tillYear': new Date().getFullYear()
};
var addCatalogPriceProps = {
'url': '/series/100',