Skip to content

Commit a3c6310

Browse files
bahossphp-coder
authored andcommitted
refactor: rewrite series sales block to react
Fix #1329
1 parent c3940f2 commit a3c6310

File tree

3 files changed

+255
-3
lines changed

3 files changed

+255
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//
2+
// IMPORTANT:
3+
// You must update ResourceUrl.RESOURCES_VERSION each time whenever you're modified this file!
4+
//
5+
6+
class SeriesSalesList extends React.PureComponent {
7+
8+
constructor(props) {
9+
super(props);
10+
this.state = {
11+
sales: [],
12+
hasServerError: false,
13+
};
14+
this.loadSales = this.loadSales.bind(this);
15+
}
16+
17+
componentDidMount() {
18+
this.loadSales();
19+
}
20+
21+
loadSales() {
22+
this.setState({
23+
hasServerError: false,
24+
sales: []
25+
});
26+
27+
axios.get(this.props.url)
28+
.then(response => {
29+
const data = response.data;
30+
this.setState({ sales: data });
31+
32+
})
33+
.catch(error => {
34+
console.error(error);
35+
this.setState({ hasServerError: true });
36+
});
37+
}
38+
39+
render() {
40+
return (
41+
<SeriesSalesListView
42+
l10n={this.props.l10n}
43+
hasServerError={this.state.hasServerError}
44+
sales={this.state.sales}
45+
/>
46+
)
47+
}
48+
}
49+
50+
class SeriesSalesListView extends React.PureComponent {
51+
render() {
52+
const { hasServerError, l10n, sales } = this.props;
53+
54+
return (
55+
<div className="row">
56+
<div className="col-sm-12">
57+
<h5>{ l10n['t_who_selling_series'] || 'Who was selling/buying this series' }</h5>
58+
<div className="row">
59+
<div id="loading-series-sales-failed-msg"
60+
className={ `alert alert-danger text-center col-sm-8 col-sm-offset-2 ${hasServerError ? '' : 'hidden'}` }>
61+
{ l10n['t_server_error'] || 'Server error' }
62+
</div>
63+
</div>
64+
<ul>
65+
{ sales.map((sale, index) => (
66+
<SeriesSaleItem
67+
key={sale.id}
68+
l10n={this.props.l10n}
69+
sale={sale}
70+
index={index + 1}
71+
/>
72+
))}
73+
</ul>
74+
</div>
75+
</div>
76+
)
77+
}
78+
}
79+
80+
class SeriesSaleItem extends React.PureComponent {
81+
render() {
82+
const { sale, index, l10n } = this.props;
83+
const hasBuyer = !!sale.buyerName;
84+
const hasCondition = !!sale.condition;
85+
const hasDate = !!sale.date;
86+
const hasTransactionUrl = !!sale.transactionUrl;
87+
const hasSecondPrice = !!sale.secondPrice;
88+
89+
return (
90+
<li id={ `series-sale-${index}-info` }>
91+
{ hasDate && sale.date }
92+
{' '}
93+
<ParticipantLink url={sale.sellerUrl} name={sale.sellerName} />
94+
{' '}
95+
{ hasBuyer ?
96+
(l10n['t_sold_to'] || 'sold to')
97+
: (l10n['t_was_selling'] || 'was selling for')
98+
}
99+
{' '}
100+
{ hasBuyer && (<ParticipantLink url={sale.buyerUrl} name={sale.buyerName} />) }
101+
{' '}
102+
{ hasBuyer && (l10n['t_sold_for'] || 'for') }
103+
{' '}
104+
{ hasTransactionUrl ?
105+
<a href={sale.transactionUrl} id={ `series-sale-${index}-transaction` } rel="nofollow">
106+
{ `${sale.firstPrice}\u00A0${sale.firstCurrency}` }
107+
{ hasSecondPrice && `(${sale.secondPrice}\u00A0${sale.secondCurrency})` }
108+
</a>
109+
: <React.Fragment>
110+
{ `${sale.firstPrice}\u00A0${sale.firstCurrency}` }
111+
{ hasSecondPrice && `(${sale.secondPrice}\u00A0${sale.secondCurrency})` }
112+
</React.Fragment>
113+
}
114+
{' '}
115+
{ hasCondition && (sale.condition !== 'CANCELLED' ? sale.condition : (l10n['t_cancelled'] || 'cancelled')) }
116+
</li>
117+
)
118+
}
119+
}
120+
121+
class ParticipantLink extends React.PureComponent {
122+
render() {
123+
const { name, url } = this.props;
124+
const hasUrl = !!url;
125+
return (
126+
hasUrl ?
127+
<a href={url} rel="nofollow">{ name }</a>
128+
: name
129+
)
130+
}
131+
}

src/main/java/ru/mystamps/web/feature/site/ResourceUrl.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public final class ResourceUrl {
3434
// MUST be updated when any of our resources were modified
3535
public static final String RESOURCES_VERSION = "v0.4.4.1";
3636

37-
// CheckStyle: ignore LineLength for next 15 lines
37+
// CheckStyle: ignore LineLength for next 18 lines
3838
private static final String CATALOG_UTILS_JS = "/public/js/" + RESOURCES_VERSION + "/CatalogUtils.min.js";
3939
private static final String COLLECTION_INFO_JS = "/public/js/" + RESOURCES_VERSION + "/collection/info.min.js";
4040
private static final String DATE_UTILS_JS = "/public/js/" + RESOURCES_VERSION + "/DateUtils.min.js";
@@ -49,6 +49,8 @@ public final class ResourceUrl {
4949
private static final String CATALOG_PRICE_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/AddCatalogPriceForm.js";
5050
private static final String CATALOG_NUMBERS_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/AddCatalogNumbersForm.js";
5151
private static final String RELEASE_YEAR_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/AddReleaseYearForm.js";
52+
private static final String SERIES_SALES_LIST_JS = "/public/js/" + RESOURCES_VERSION + "/components/SeriesSalesList.js";
53+
5254
private static final String BOOTSTRAP_LANGUAGE = "https://cdn.jsdelivr.net/gh/usrz/bootstrap-languages@3ac2a3d2b27ac43a471cd99e79d378a03b2c6b5f/languages.min.css";
5355
private static final String FAVICON_ICO = "/favicon.ico";
5456

@@ -88,6 +90,8 @@ public static void exposeResourcesToView(Map<String, String> resources, String h
8890
put(resources, host, "CATALOG_PRICE_FORM_JS", CATALOG_PRICE_FORM_JS);
8991
put(resources, host, "CATALOG_NUMBERS_FORM_JS", CATALOG_NUMBERS_FORM_JS);
9092
put(resources, host, "RELEASE_YEAR_FORM_JS", RELEASE_YEAR_FORM_JS);
93+
put(resources, host, "SERIES_SALES_LIST_JS", SERIES_SALES_LIST_JS);
94+
9195
}
9296

9397
// see also MvcConfig.addResourceHandlers()

src/main/webapp/WEB-INF/views/series/info.html

+119-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
<html lang="en" th:lang="${#locale.language == 'ru' ? 'ru' : 'en'}"
33
xmlns="http://www.w3.org/1999/xhtml"
44
xmlns:th="http://www.thymeleaf.org"
5-
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
5+
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
6+
xmlns:togglz="https://github.com/heneke/thymeleaf-extras-togglz">
67
<head>
78
<meta charset="utf-8" />
89
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -559,7 +560,7 @@ <h5 class="text-center" th:text="#{t_similar_series}">
559560
/*/-->
560561
</div>
561562

562-
<div class="row" th:if="${not #lists.isEmpty(seriesSales)}" sec:authorize="hasAuthority('VIEW_SERIES_SALES')">
563+
<div class="row" th:if="${not #lists.isEmpty(seriesSales)}" sec:authorize="hasAuthority('VIEW_SERIES_SALES')" togglz:inactive="USE_REACT">
563564
<div class="col-sm-12">
564565
<h5 th:text="#{t_who_selling_series}">Who was selling/buying this series</h5>
565566
<ul th:remove="all-but-first">
@@ -623,6 +624,8 @@ <h5 th:text="#{t_who_selling_series}">Who was selling/buying this series</h5>
623624
</div>
624625
</div>
625626

627+
<div id="series-sales-list" sec:authorize="hasAuthority('VIEW_SERIES_SALES')" togglz:active="USE_REACT"></div>
628+
626629
<div class="row" sec:authorize="hasAuthority('ADD_SERIES_SALES')">
627630
<div class="col-sm-12">
628631
<h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying this series</h5>
@@ -999,6 +1002,105 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
9991002

10001003
responseCount++;
10011004

1005+
return new Promise(function delayExecution(resolve) {
1006+
setTimeout(resolve, 500 /* 0.5 second */);
1007+
1008+
}).then(function returnResponse() {
1009+
return stubResponse.status == 500 ? Promise.reject(stubResponse) : Promise.resolve(stubResponse);
1010+
});
1011+
},
1012+
get: function (url) {
1013+
var possibleOutcomes = [ 'success'];
1014+
var outcome = possibleOutcomes[responseCount % possibleOutcomes.length];
1015+
var possibleResponses = {
1016+
'/series/100': {
1017+
'success': {
1018+
status: 200,
1019+
data: [
1020+
{
1021+
id: 1,
1022+
sellerName: 'James Alan Hetfield',
1023+
sellerUrl: 'http://example.com/james-alan-hetfield',
1024+
buyerName: 'Eicca Toppinen',
1025+
buyerUrl: 'http://example.com/eicca-toppinen',
1026+
transactionUrl: 'http://example.com/james-alan-hetfield/selling-stamps',
1027+
firstPrice: 100,
1028+
firstCurrency: 'USD',
1029+
condition: 'CANCELLED'
1030+
},
1031+
{
1032+
id: 2,
1033+
sellerName: 'James Alan Hetfield',
1034+
sellerUrl: 'http://example.com/james-alan-hetfield',
1035+
transactionUrl: 'http://example.com/james-alan-hetfield/selling-stamps',
1036+
firstPrice: 100,
1037+
firstCurrency: 'USD',
1038+
secondPrice: 650,
1039+
secondCurrency: 'RUB',
1040+
condition: 'CANCELLED'
1041+
},
1042+
{
1043+
id: 3,
1044+
date: '02.02.2002',
1045+
sellerName: 'Tommy Lee Jones',
1046+
sellerUrl: 'http://example.com/tommy-lee-jones',
1047+
transactionUrl: 'http://example.com/tommy-lee-jones/selling-stamps',
1048+
firstPrice: 200,
1049+
firstCurrency: 'USD',
1050+
condition: 'MNH'
1051+
},
1052+
{
1053+
id: 4,
1054+
date: '02.02.2002',
1055+
sellerName: 'Tommy Lee Jones',
1056+
sellerUrl: 'http://example.com/tommy-lee-jones',
1057+
transactionUrl: 'http://example.com/tommy-lee-jones/selling-stamps',
1058+
firstPrice: 200,
1059+
firstCurrency: 'USD',
1060+
secondPrice: 1300,
1061+
secondCurrency: 'RUB',
1062+
},
1063+
{
1064+
id: 5,
1065+
date: '03.02.2002',
1066+
sellerName: 'Eicca Toppinen',
1067+
sellerUrl: 'http://example.com/eicca-toppinen',
1068+
transactionUrl: 'http://example.com/tommy-lee-jones/selling-stamps',
1069+
firstPrice: 300,
1070+
firstCurrency: 'USD',
1071+
secondPrice: 1560,
1072+
secondCurrency: 'RUB',
1073+
},
1074+
{
1075+
id: 6,
1076+
date: '03.02.2002',
1077+
sellerName: 'Eicca Toppinen',
1078+
sellerUrl: 'http://example.com/eicca-toppinen',
1079+
buyerName: 'Kurt Cobain',
1080+
firstPrice: 300,
1081+
firstCurrency: 'USD',
1082+
secondPrice: 1560,
1083+
secondCurrency: 'RUB',
1084+
}
1085+
]
1086+
}
1087+
}
1088+
};
1089+
var stubResponse;
1090+
1091+
switch (outcome) {
1092+
case 'success':
1093+
stubResponse = possibleResponses[url][outcome];
1094+
break;
1095+
default:
1096+
stubResponse = {
1097+
status: 500,
1098+
statusText: 'Fake Server Error'
1099+
};
1100+
}
1101+
1102+
responseCount++;
1103+
10021104
return new Promise(function delayExecution(resolve) {
10031105
setTimeout(resolve, 500 /* 0.5 second */);
10041106

@@ -1019,6 +1121,8 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
10191121
<script src="../../../../../../target/classes/js/components/AddReleaseYearForm.js" th:src="${RELEASE_YEAR_FORM_JS}"></script>
10201122
<script src="../../../../../../target/classes/js/components/AddCatalogPriceForm.js" th:src="${CATALOG_PRICE_FORM_JS}"></script>
10211123
<script src="../../../../../../target/classes/js/components/AddCatalogNumbersForm.js" th:src="${CATALOG_NUMBERS_FORM_JS}"></script>
1124+
<script src="../../../../../../target/classes/js/components/SeriesSalesList.js" th:src="${SERIES_SALES_LIST_JS}"></script>
1125+
10221126

10231127
<script th:inline="javascript">
10241128
/*[+
@@ -1057,6 +1161,12 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
10571161
't_add': [[ #{t_add} ]]
10581162
}
10591163
};
1164+
var seriesSalesListProps = {
1165+
'url': [[ '__@{${INFO_SERIES_PAGE}(id=${series.id})}__' ]],
1166+
'l10n': {
1167+
't_server_error': [[ #{t_server_error} ]],
1168+
}
1169+
};
10601170
+]*/
10611171

10621172
/*[- */
@@ -1074,10 +1184,17 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
10741184
'url': '/series/100',
10751185
'l10n': {}
10761186
};
1187+
1188+
var seriesSalesListProps = {
1189+
'url': '/series/100',
1190+
'l10n': {}
1191+
};
10771192
/* -]*/
10781193

10791194
renderComponent(AddCatalogPriceForm, addCatalogPriceProps, 'add-catalog-price');
10801195
renderComponent(AddCatalogNumbersForm, addCatalogNumbersProps, 'add-catalog-numbers');
1196+
renderComponent(SeriesSalesList, seriesSalesListProps, 'series-sales-list');
1197+
10811198

10821199
/*[# th:if="${series.releaseYear == null}"]*/
10831200
/*[+

0 commit comments

Comments
 (0)