Skip to content

Commit cefbc0e

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

File tree

3 files changed

+273
-3
lines changed

3 files changed

+273
-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

+137-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

@@ -1105,6 +1207,39 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
11051207
</th:block>
11061208
/*/-->
11071209

1210+
<!--/*/
1211+
<th:block sec:authorize="hasAuthority('VIEW_SERIES_SALES')" togglz:active="USE_REACT">
1212+
/*/-->
1213+
<script src="../../../../../../target/classes/js/components/SeriesSalesList.js" th:src="${SERIES_SALES_LIST_JS}"></script>
1214+
1215+
<script th:inline="javascript">
1216+
/*[+
1217+
var seriesSalesListProps = {
1218+
'url': [[ '__@{${INFO_SERIES_PAGE}(id=${series.id})}__' ]],
1219+
'l10n': {
1220+
't_server_error': [[ #{t_server_error} ]],
1221+
't_who_selling_series': [[ #{t_who_selling_series} ]],
1222+
't_sold_to': [[ #{t_sold_to} ]],
1223+
't_sold_for': [[ #{t_sold_for} ]],
1224+
't_was_selling': [[ #{t_was_selling} ]],
1225+
't_cancelled': [[ #{t_cancelled} ]]
1226+
}
1227+
};
1228+
+]*/
1229+
1230+
/*[- */
1231+
var seriesSalesListProps = {
1232+
'url': '/series/100',
1233+
'l10n': {}
1234+
};
1235+
/* -]*/
1236+
1237+
renderComponent(SeriesSalesList, seriesSalesListProps, 'series-sales-list');
1238+
</script>
1239+
<!--/*/
1240+
</th:block>
1241+
/*/-->
1242+
11081243
<!--/*/
11091244
<th:block sec:authorize="hasAuthority('ADD_COMMENTS_TO_SERIES')" th:if="${series.comment == null}">
11101245
/*/-->

0 commit comments

Comments
 (0)