Skip to content

Commit f27faf0

Browse files
committed
task: add a prototype for a form for hiding the images
Part of #1383
1 parent d59fcef commit f27faf0

File tree

9 files changed

+196
-2
lines changed

9 files changed

+196
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//
2+
// IMPORTANT:
3+
// You must update ResourceUrl.RESOURCES_VERSION each time whenever you're modified this file!
4+
//
5+
import React from 'react';
6+
7+
// @todo #1383 HideImageForm: add tests
8+
class HideImageForm extends React.PureComponent {
9+
constructor(props) {
10+
super(props);
11+
this.state = {
12+
imageId: null,
13+
validationErrors: [],
14+
hasServerError: false,
15+
isDisabled: false
16+
};
17+
this.handleSubmit = this.handleSubmit.bind(this);
18+
this.handleChange = this.handleChange.bind(this);
19+
}
20+
21+
handleChange(event) {
22+
event.preventDefault();
23+
this.setState({
24+
comment: event.target.value
25+
});
26+
}
27+
28+
handleSubmit(event) {
29+
event.preventDefault();
30+
31+
this.setState({
32+
isDisabled: true,
33+
hasServerError: false,
34+
validationErrors: []
35+
});
36+
37+
axios.patch(
38+
this.props.url, [
39+
{
40+
op: 'add',
41+
path: '/comment',
42+
value: this.state.imageId
43+
}
44+
],
45+
{
46+
headers: {
47+
[this.props.csrfHeaderName]: this.props.csrfTokenValue,
48+
'Cache-Control': 'no-store'
49+
},
50+
validateStatus: status => {
51+
return status == 204 || status == 400;
52+
}
53+
})
54+
.then(response => {
55+
const data = response.data;
56+
57+
if (data.hasOwnProperty('fieldErrors')) {
58+
const fieldErrors = [];
59+
if (data.fieldErrors.value) {
60+
fieldErrors.push(...data.fieldErrors.value);
61+
}
62+
this.setState({
63+
isDisabled: false,
64+
validationErrors: fieldErrors
65+
});
66+
return;
67+
}
68+
69+
// no need to reset the state as page will be reloaded
70+
window.location.reload();
71+
})
72+
.catch(error => {
73+
console.error(error);
74+
this.setState({ isDisabled: false, hasServerError: true });
75+
});
76+
}
77+
render() {
78+
return (
79+
<HideImageFormView
80+
l10n={this.props.l10n}
81+
imageIds={this.props.imageIds}
82+
handleSubmit={this.handleSubmit}
83+
hasServerError={this.state.hasServerError}
84+
handleChange={this.handleChange}
85+
validationErrors={this.state.validationErrors}
86+
isDisabled={this.state.isDisabled}
87+
/>
88+
);
89+
}
90+
}
91+
92+
class HideImageFormView extends React.PureComponent {
93+
render() {
94+
const {handleSubmit, hasServerError, handleChange, validationErrors = [], isDisabled, l10n = {}, imageIds} = this.props;
95+
const hasValidationErrors = validationErrors.length > 0;
96+
return (
97+
<div className="col-sm-10">
98+
<form id="hide-image-form" className="form-horizontal" onSubmit={ handleSubmit }>
99+
100+
{ hasServerError &&
101+
<div id="hide-image-failed-msg"
102+
role="alert"
103+
className="alert alert-danger text-center col-sm-8 col-sm-offset-2">
104+
{ l10n['t_server_error'] || 'Server error' }
105+
</div>
106+
}
107+
108+
<div className={ `form-group form-group-sm ${hasValidationErrors ? 'has-error' : ''}` }>
109+
<label htmlFor="hide-image-id" className="control-label col-sm-3 required-field">
110+
{ l10n['t_image_id'] || 'Image ID' }
111+
</label>
112+
<div className="col-sm-4">
113+
<select
114+
id="hide-image-id"
115+
className="form-control"
116+
required="required"
117+
onChange={ handleChange }>
118+
<option value="">{ l10n['t_not_chosen'] || 'Not chosen' }</option>
119+
{
120+
this.props.imageIds.map(imageId =>
121+
<option key={ imageId } value="{ imageId }">{ imageId }</option>
122+
)
123+
}
124+
</select>
125+
{ hasValidationErrors &&
126+
<span id="hide-image-id.errors" className="help-block">
127+
{ validationErrors.join(', ') }
128+
</span>
129+
}
130+
</div>
131+
</div>
132+
133+
<div className="form-group from-group-sm">
134+
<div className="col-sm-offset-3 col-sm-8">
135+
<button
136+
type="submit"
137+
className="btn btn-default btn-sm"
138+
disabled={ isDisabled }>
139+
{ l10n['t_hide_image'] || 'Hide image' }
140+
</button>
141+
</div>
142+
</div>
143+
</form>
144+
</div>
145+
);
146+
}
147+
}
148+
149+
window.HideImageForm = HideImageForm;

src/main/frontend/webpack.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
AddCatalogPriceForm: SRC_DIR + 'AddCatalogPriceForm.js',
1212
AddCommentForm: SRC_DIR + 'AddCommentForm.js',
1313
AddReleaseYearForm: SRC_DIR + 'AddReleaseYearForm.js',
14+
HideImageForm: SRC_DIR + 'HideImageForm.js',
1415
SeriesSaleImportForm: SRC_DIR + 'SeriesSaleImportForm.js',
1516
SeriesSalesList: SRC_DIR + 'SeriesSalesList.js',
1617
SimilarSeriesForm: SRC_DIR + 'SimilarSeriesForm.js'

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

+3-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.5.4";
3636

37-
// CheckStyle: ignore LineLength for next 16 lines
37+
// CheckStyle: ignore LineLength for next 17 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";
@@ -48,6 +48,7 @@ public final class ResourceUrl {
4848
private static final String CATALOG_PRICE_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/AddCatalogPriceForm.min.js";
4949
private static final String CATALOG_NUMBERS_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/AddCatalogNumbersForm.min.js";
5050
private static final String RELEASE_YEAR_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/AddReleaseYearForm.min.js";
51+
private static final String HIDE_IMAGE_FORM_JS = "/public/js/" + RESOURCES_VERSION + "/components/HideImageForm.min.js";
5152
private static final String SERIES_SALES_LIST_JS = "/public/js/" + RESOURCES_VERSION + "/components/SeriesSalesList.min.js";
5253

5354
private static final String BOOTSTRAP_LANGUAGE = "https://cdn.jsdelivr.net/gh/usrz/bootstrap-languages@3ac2a3d2b27ac43a471cd99e79d378a03b2c6b5f/languages.min.css";
@@ -89,6 +90,7 @@ public static void exposeResourcesToView(Map<String, String> resources, String h
8990
put(resources, host, "CATALOG_PRICE_FORM_JS", CATALOG_PRICE_FORM_JS);
9091
put(resources, host, "CATALOG_NUMBERS_FORM_JS", CATALOG_NUMBERS_FORM_JS);
9192
put(resources, host, "RELEASE_YEAR_FORM_JS", RELEASE_YEAR_FORM_JS);
93+
put(resources, host, "HIDE_IMAGE_FORM_JS", HIDE_IMAGE_FORM_JS);
9294
put(resources, host, "SERIES_SALES_LIST_JS", SERIES_SALES_LIST_JS);
9395

9496
}

src/main/java/ru/mystamps/web/support/spring/security/Authority.java

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public final class Authority {
3131
public static final GrantedAuthority CREATE_CATEGORY = new SimpleGrantedAuthority(StringAuthority.CREATE_CATEGORY);
3232
public static final GrantedAuthority CREATE_COUNTRY = new SimpleGrantedAuthority(StringAuthority.CREATE_COUNTRY);
3333
public static final GrantedAuthority CREATE_SERIES = new SimpleGrantedAuthority(StringAuthority.CREATE_SERIES);
34+
public static final GrantedAuthority HIDE_IMAGE = new SimpleGrantedAuthority(StringAuthority.HIDE_IMAGE);
3435
public static final GrantedAuthority DOWNLOAD_IMAGE = new SimpleGrantedAuthority(StringAuthority.DOWNLOAD_IMAGE);
3536
public static final GrantedAuthority IMPORT_SERIES = new SimpleGrantedAuthority(StringAuthority.IMPORT_SERIES);
3637
public static final GrantedAuthority IMPORT_SERIES_SALES = new SimpleGrantedAuthority(StringAuthority.IMPORT_SERIES_SALES);

src/main/java/ru/mystamps/web/support/spring/security/CustomUserDetailsService.java

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ private static Collection<? extends GrantedAuthority> getAuthorities(UserDetails
7777
authorities.add(Authority.ADD_SERIES_PRICE);
7878
authorities.add(Authority.ADD_SERIES_SALES);
7979
authorities.add(Authority.DOWNLOAD_IMAGE);
80+
authorities.add(Authority.HIDE_IMAGE);
8081
authorities.add(Authority.IMPORT_SERIES);
8182
authorities.add(Authority.IMPORT_SERIES_SALES);
8283
authorities.add(Authority.MANAGE_TOGGLZ);

src/main/java/ru/mystamps/web/support/spring/security/StringAuthority.java

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public final class StringAuthority {
2828
public static final String CREATE_COUNTRY = "CREATE_COUNTRY";
2929
public static final String CREATE_SERIES = "CREATE_SERIES";
3030
public static final String DOWNLOAD_IMAGE = "DOWNLOAD_IMAGE";
31+
public static final String HIDE_IMAGE = "HIDE_IMAGE";
3132
public static final String IMPORT_SERIES = "IMPORT_SERIES";
3233
public static final String IMPORT_SERIES_SALES = "IMPORT_SERIES_SALES";
3334
public static final String MANAGE_TOGGLZ = "MANAGE_TOGGLZ";

src/main/resources/ru/mystamps/i18n/Messages.properties

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ t_image_id = Image ID
146146
t_add_image = Add image
147147
t_replace_image = Replace image
148148
t_add_image_error = This series has enough images. Please, contact with admin if you want to add more images
149+
t_hide_image = Hide image
149150
t_who_selling_series = Who was selling/buying this series
150151
t_add_info_who_selling_series = Add info about selling/buying this series
151152
t_dd_mm_yyyy = dd.mm.yyyy

src/main/resources/ru/mystamps/i18n/Messages_ru.properties

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ t_image_id = ID изображения
145145
t_add_image = Добавить изображение
146146
t_replace_image = Заменить изображение
147147
t_add_image_error = Эта серия содержит все необходимые изображения. Пожалуйста, свяжитесь с администратором, если вы хотите добавить больше изображений
148+
t_hide_image = Скрыть изображение
148149
t_who_selling_series = Покупки и продажи этой серии
149150
t_add_info_who_selling_series = Добавить покупки и продажи этой серии
150151
t_dd_mm_yyyy = дд.мм.гггг

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

+38-1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@
172172
</div>
173173
</div>
174174

175+
<div id="hide-image" class="row" sec:authorize="hasAuthority('HIDE_IMAGE')" th:if="${#lists.size(series.imageIds) > 1}"></div>
176+
175177
<!--/* @todo #1356 Hidden images: add integration tests */-->
176178
<!--/* @todo #1356 Hidden images: allow to replace a hidden image */-->
177179
<!--/* @todo #1356 Hidden images: admin can hide an image */-->
@@ -882,7 +884,7 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
882884
th:src="${SERIES_INFO_JS}"></script>
883885

884886
<!--/*/
885-
<th:block sec:authorize="hasAnyAuthority('IMPORT_SERIES_SALES', 'MARK_SIMILAR_SERIES', 'CREATE_SERIES', 'ADD_COMMENTS_TO_SERIES')">
887+
<th:block sec:authorize="hasAnyAuthority('IMPORT_SERIES_SALES', 'MARK_SIMILAR_SERIES', 'CREATE_SERIES', 'ADD_COMMENTS_TO_SERIES', 'HIDE_IMAGE')">
886888
/*/-->
887889
<script src="https://unpkg.com/[email protected]/umd/react.development.js" th:src="${REACT_JS}"></script>
888890
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" th:src="${REACT_DOM_JS}"></script>
@@ -1334,5 +1336,40 @@ <h5 th:text="#{t_add_info_who_selling_series}">Add info about selling/buying thi
13341336
<!--/*/
13351337
</th:block>
13361338
/*/-->
1339+
1340+
<!--/*/
1341+
<th:block sec:authorize="hasAuthority('HIDE_IMAGE')" th:if="${#lists.size(series.imageIds) > 1}">
1342+
/*/-->
1343+
<script src="../../../../../../target/classes/js/components/HideImageForm.min.js" th:src="${HIDE_IMAGE_FORM_JS}"></script>
1344+
<script th:inline="javascript">
1345+
/*[+
1346+
var hideImageProps = {
1347+
'url': [[ '__@{${INFO_SERIES_PAGE}(id=${series.id})}__' ]],
1348+
'imageIds': [[ ${series.imageIds} ]],
1349+
'csrfHeaderName': [[ ${_csrf.headerName} ]],
1350+
'csrfTokenValue': [[ ${_csrf.token} ]],
1351+
'l10n': {
1352+
't_image_id': [[ #{t_image_id} ]],
1353+
't_server_error': [[ #{t_server_error} ]],
1354+
't_not_chosen': [[ #{t_not_chosen} ]],
1355+
't_hide_image': [[ #{t_hide_image} ]]
1356+
}
1357+
};
1358+
+]*/
1359+
1360+
/*[- */
1361+
var hideImageProps = {
1362+
'url': '/series/100',
1363+
'l10n': {},
1364+
'imageIds': [ 1 ]
1365+
};
1366+
/* -]*/
1367+
1368+
renderComponent(HideImageForm, hideImageProps, 'hide-image');
1369+
</script>
1370+
<!--/*/
1371+
</th:block>
1372+
/*/-->
1373+
13371374
</body>
13381375
</html>

0 commit comments

Comments
 (0)