Skip to content

Commit

Permalink
Add filter by tags into scene page
Browse files Browse the repository at this point in the history
  • Loading branch information
callemand committed Oct 13, 2023
1 parent bf20f7d commit 9aa839f
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 48 deletions.
16 changes: 0 additions & 16 deletions front/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
"react-dnd-touch-backend": "^16.0.1",
"react-select": "^4.3.1",
"react-slider": "^2.0.6",
"react-tag-input-component": "^2.0.2",
"unistore": "^3.5.2",
"useragent-parser-js": "^1.0.3",
"uuid": "^3.4.0"
Expand Down
31 changes: 23 additions & 8 deletions front/src/components/layout/CardFilter.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { Fragment } from 'preact';
import { Text } from 'preact-i18n';
import Select from 'react-select';

const CardFilter = ({ changeOrderDir, orderValue = 'asc', search, searchValue, searchPlaceHolder }) => (
const CardFilter = ({
changeOrderDir,
orderValue = 'asc',
search,
searchValue,
searchPlaceHolder,
tags,
searchTags
}) => (
<Fragment>
<select onChange={changeOrderDir} class="form-control custom-select w-auto">
<option value="asc" selected={orderValue === 'asc'}>
Expand All @@ -11,17 +20,23 @@ const CardFilter = ({ changeOrderDir, orderValue = 'asc', search, searchValue, s
<Text id="global.orderDirDesc" />
</option>
</select>
{tags && (
<div class="ml-2 w-50">
<Select
options={tags.map(tag => ({ value: tag.name, label: tag.name }))}
onChange={tags => searchTags(tags.map(tag => tag.value))}
closeMenuOnSelect={false}
isMulti
formatCreateLabel={inputValue => <Text id="editScene.createTag" fields={{ tagName: inputValue }} />}
/>
</div>
)}

<div class="input-icon ml-2">
<span class="input-icon-addon">
<i class="fe fe-search" />
</span>
<input
type="text"
class="form-control w-10"
placeholder={searchPlaceHolder}
onInput={search}
value={searchValue}
/>
<input type="text" class="form-control" placeholder={searchPlaceHolder} onInput={search} value={searchValue} />
</div>
</Fragment>
);
Expand Down
1 change: 1 addition & 0 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@
"editDescriptionPlaceholder": "Enter a scene description",
"tagsTitle": "Tags",
"editTagsPlaceholder": "Enter a scene tags",
"createTag": "Create tag: '{{tagName}}'",
"startButton": "Start",
"saveButton": "Save",
"deleteButton": "Delete",
Expand Down
1 change: 1 addition & 0 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@
"descriptionTitle": "Description",
"iconLabel": "Icône",
"editDescriptionPlaceholder": "Entrez une description pour la scène",
"createTag": "Créer le tag : '{{tagName}}'",
"tagsTitle": "Tags",
"editTagsPlaceholder": "Entrez un tag pour la scène",
"startButton": "Démarrer",
Expand Down
2 changes: 1 addition & 1 deletion front/src/routes/scene/SceneCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class SceneCard extends Component {
<div class="loader" />
<div class="dimmer-content h-100">
<div class="card h-100 d-flex flex-column justify-content-between">
<div class="card-body pb-0 pt-3 pl-3 pr-3 text-center h-100">
<div class="card-body p-3 text-center h-100 d-flex flex-column">
<div class={style.scene_icon}>
<i class={`fe fe-${props.scene.icon}`} />
</div>
Expand Down
4 changes: 3 additions & 1 deletion front/src/routes/scene/ScenePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ const ScenePage = ({ children, ...props }) => (
search={props.debouncedSearch}
searchValue={props.sceneSearch}
searchPlaceHolder={<Text id="scene.searchPlaceholder" />}
tags={props.tags}
searchTags={props.searchTags}
/>
</Localizer>
<Link href="/dashboard/scene/new" class="btn btn-outline-primary ml-2">
<Link href="/dashboard/scene/new" class={cx('btn', 'btn-outline-primary', 'ml-2', style.newButton)}>
<Text id="scene.newButton" /> <i class="fe fe-plus" />
</Link>
</div>
Expand Down
3 changes: 3 additions & 0 deletions front/src/routes/scene/edit-scene/Settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class Settings extends Component {
isMulti
options={props.tags && props.tags.map(tag => ({ value: tag.name, label: tag.name }))}
onChange={tags => props.setTags(tags.map(tag => tag.value))}
formatCreateLabel={inputValue => (
<Text id="editScene.createTag" fields={{ tagName: inputValue }} />
)}
/>
</Localizer>
</div>
Expand Down
27 changes: 26 additions & 1 deletion front/src/routes/scene/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class Scene extends Component {
if (this.state.sceneSearch && this.state.sceneSearch.length) {
params.search = this.state.sceneSearch;
}
if (this.state.sceneTagSearch && this.state.sceneTagSearch.length) {
params.searchTags = this.state.sceneTagSearch.join(',');
}
const scenes = await this.props.httpClient.get('/api/v1/scene', params);
this.setState({
scenes,
Expand All @@ -31,12 +34,30 @@ class Scene extends Component {
});
}
};
getTags = async () => {
try {
const tags = await this.props.httpClient.get(`/api/v1/tag_scene`);
this.setState({
tags
});
} catch (e) {
console.error(e);
}
};
search = async e => {
await this.setState({
sceneSearch: e.target.value
});
await this.getScenes();
};
searchTags = async tags => {
console.log(tags);
await this.setState({
sceneTagSearch: tags
});
await this.getScenes();
};

changeOrderDir = async e => {
await this.setState({
getScenesOrderDir: e.target.value
Expand Down Expand Up @@ -88,16 +109,18 @@ class Scene extends Component {
getScenesOrderDir: 'asc',
scenes: [],
sceneSearch: null,
sceneTagSearch: null,
loading: true
};
this.debouncedSearch = debounce(this.search.bind(this), 200);
}

componentWillMount() {
this.getScenes();
this.getTags();
}

render(props, { scenes, loading, getError }) {
render(props, { scenes, loading, getError, tags }) {
return (
<ScenePage
httpClient={props.httpClient}
Expand All @@ -108,6 +131,8 @@ class Scene extends Component {
debouncedSearch={this.debouncedSearch}
changeOrderDir={this.changeOrderDir}
switchActiveScene={this.switchActiveScene}
tags={tags}
searchTags={this.searchTags}
/>
);
}
Expand Down
5 changes: 5 additions & 0 deletions front/src/routes/scene/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@
}

.descriptionSceneEllipsis {
flex-grow: 2;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
}

.newButton {
min-width: min-content;
}
38 changes: 18 additions & 20 deletions server/lib/scene/scene.get.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { Op } = require('sequelize');
const Sequelize = require('sequelize');
const db = require('../../models');

const DEFAULT_OPTIONS = {
Expand Down Expand Up @@ -46,30 +47,27 @@ async function get(options) {
})),
};
}

const scenes = await db.Scene.findAll(queryParams);

let scenesPlain = scenes.map((scene) => scene.get({ plain: true }));

if (optionsWithDefault.search) {
scenesPlain = scenesPlain.filter((scene) => {
if (scene.name.toLowerCase().includes(optionsWithDefault.search.toLowerCase())) {
return scene;
}
const tagsFound = scene.tags.find((tag) => {
if (tag.name.toLowerCase().includes(optionsWithDefault.search.toLowerCase())) {
return true;
}
return false;
});
if (tagsFound) {
return scene;
}
return null;
queryParams.where = Sequelize.where(Sequelize.fn('lower', Sequelize.col('t_scene.name')), {
[Op.like]: `%${optionsWithDefault.search}%`,
});
}

if (optionsWithDefault.searchTags) {
const tags = await db.TagScene.findAll({
fields: 'scene_id',
where: {
[Op.or]: optionsWithDefault.searchTags.split(',').map((tag) => ({ name: { [Op.like]: `%${tag}%` } })),
},
});
queryParams.where = {
[Op.or]: tags.map((tag) => tag.get({ plain: true })).map((tag) => ({ id: tag.scene_id })),
};
}

return scenesPlain;
const scenes = await db.Scene.findAll(queryParams);

return scenes.map((scene) => scene.get({ plain: true }));
}

module.exports = {
Expand Down

0 comments on commit 9aa839f

Please sign in to comment.