Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tag on scene #1900

Merged
merged 34 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 103 additions & 69 deletions front/cypress/e2e/routes/scene/Scene.cy.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,7 @@
describe('Scene view', () => {
before(() => {
cy.login();
const serverUrl = Cypress.env('serverUrl');
cy.request({
method: 'GET',
url: `${serverUrl}/api/v1/room`
}).then(res => {
const device = {
name: 'One device',
external_id: 'one-device',
selector: 'one-device',
room_id: res.body[0].id,
features: [
{
name: 'Multilevel',
category: 'light',
type: 'temperature',
external_id: 'light-temperature',
selector: 'light-temperature',
read_only: false,
keep_history: true,
has_feedback: true,
min: 0,
max: 1
}
]
};
cy.createDevice(device, 'mqtt');
});
});
beforeEach(() => {
cy.login();
});
after(() => {
// Delete all Bluetooth devices
cy.deleteDevices('mqtt');
});
it('Should create new scene', () => {
cy.visit('/dashboard/scene');
cy.contains('scene.newButton')
Expand All @@ -57,14 +23,23 @@ describe('Scene view', () => {

cy.url().should('eq', `${Cypress.config().baseUrl}/dashboard/scene/my-scene`);
});
it('Should edit the scene description', () => {
it('Should edit the scene settings', () => {
cy.visit('/dashboard/scene/my-scene');

cy.contains('editScene.editDescriptionPlaceholder').click();
cy.get('div[class*="card-header"]')
.contains('editScene.settings')
.should('have.class', 'card-title')
.click();

cy.get('input:visible').then(inputs => {
// Zone name
cy.wrap(inputs[0]).type('My scene description');
cy.get('div[class*="form-group"]').then(inputs => {
cy.wrap(inputs[0])
.find('input')
.clear()
.type('My scene name');
cy.wrap(inputs[1])
.find('input')
.type('My scene description');
cy.wrap(inputs[2]).type('My tag 1{enter}{enter}');
});

// I don't know why, but I'm unable to get this button with
Expand All @@ -73,6 +48,7 @@ describe('Scene view', () => {
cy.wrap(buttons[0]).click();
});
});

it('Should add new condition house empty', () => {
cy.visit('/dashboard/scene/my-scene');
cy.contains('editScene.addActionButton')
Expand All @@ -81,54 +57,108 @@ describe('Scene view', () => {

const i18n = Cypress.env('i18n');

cy.get('div[class*="-control"]')
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("${i18n.editScene.actions.house['is-empty']}")`)
.click(0, 0, { force: true });
cy.get('div[class*="-control"]').then(inputs => {
cy.wrap(inputs[1])
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("${i18n.editScene.actions.house['is-empty']}")`)
.click(0, 0, { force: true });
});

// I don't know why, but I'm unable to get this button with
// the text. Using the class but it's not recommended otherwise!!
cy.get('.btn-success').then(buttons => {
cy.wrap(buttons[1]).click();
});

cy.get('div[class*="-control"]')
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("My House")`)
.click(0, 0, { force: true });
cy.get('div[class*="-control"]').then(inputs => {
cy.wrap(inputs[1])
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("My House")`)
.click(0, 0, { force: true });
});
});

it('Should add new condition device set value', () => {
const serverUrl = Cypress.env('serverUrl');
cy.intercept(
{
method: 'GET',
url: `${serverUrl}/api/v1/room?expand=devices`
},
[
{
id: 'd63ce677-f5f8-47e1-816d-7aa227c863e4',
house_id: '6c1c78f0-1c26-4944-9149-77188e25d00d',
name: 'Living Room',
selector: 'living-room',
created_at: '2023-10-03T12:21:39.551Z',
updated_at: '2023-10-03T12:21:39.551Z',
devices: [
{
name: 'One device',
selector: 'one-device',
features: [
{
name: 'Multilevel',
selector: 'light-temperature',
category: 'light',
type: 'temperature',
read_only: false,
unit: null,
min: 0,
max: 1,
last_value: null,
last_value_changed: null
}
],
service: { id: '123d4d56-6cbd-4020-991f-2a0a8e0ac3e0', name: 'mqtt' }
}
]
}
]
).as('loadDevices');

cy.visit('/dashboard/scene/my-scene');
cy.contains('editScene.addActionButton')
.should('have.class', 'btn-outline-primary')
.click();

const i18n = Cypress.env('i18n');

cy.get('div[class*="-control"]')
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("${i18n.editScene.actions.device['set-value']}")`)
.click(0, 0, { force: true });
cy.get('div[class*="-control"]').then(inputs => {
cy.wrap(inputs[1])
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("${i18n.editScene.actions.device['set-value']}")`)
.click(0, 0, { force: true });
});

// I don't know why, but I'm unable to get this button with
// the text. Using the class but it's not recommended otherwise!!
cy.get('.btn-success').then(buttons => {
cy.wrap(buttons[1]).click();
});

cy.get('div[class*="-control"]')
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("Multilevel")`)
.click(0, 0, { force: true });
cy.wait('@loadDevices');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(100);

cy.get('div[class*="-control"]').then(inputs => {
cy.wrap(inputs[1])
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("Multilevel")`)
.click(0, 0, { force: true });
cy.log('4');
});
});

it('Should add new calendar event trigger', () => {
cy.visit('/dashboard/scene/my-scene');
cy.contains('editScene.addNewTriggerButton')
Expand All @@ -137,12 +167,14 @@ describe('Scene view', () => {

const i18n = Cypress.env('i18n');

cy.get('div[class*="-control"]')
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("${i18n.editScene.triggers.calendar['event-is-coming']}")`)
.click(0, 0, { force: true });
cy.get('div[class*="-control"]').then(inputs => {
cy.wrap(inputs[1])
.click(0, 0, { force: true })
.get('[class*="-menu"]')
.find('[class*="-option"]')
.filter(`:contains("${i18n.editScene.triggers.calendar['event-is-coming']}")`)
.click(0, 0, { force: true });
});

// I don't know why, but I'm unable to get this button with
// the text. Using the class but it's not recommended otherwise!!
Expand All @@ -156,6 +188,7 @@ describe('Scene view', () => {
cy.wrap(selects[2]).select('minute');
});
});

it('Should disable scene', () => {
cy.visit('/dashboard/scene');

Expand Down Expand Up @@ -202,6 +235,7 @@ describe('Scene view', () => {

cy.url().should('eq', `${Cypress.config().baseUrl}/dashboard/scene/my-duplicated-scene`);
});

it('Should delete existing scene', () => {
cy.login();
cy.visit('/dashboard/scene/my-scene');
Expand Down
3 changes: 2 additions & 1 deletion front/src/actions/createScene.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ function createActions(store) {
newScene: {
name: '',
icon: null,
actions: [[]]
actions: [[]],
tags: []
},
newSceneErrors: null,
createSceneStatus: null
Expand Down
9 changes: 2 additions & 7 deletions front/src/components/layout/CardFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,12 @@ const CardFilter = ({ changeOrderDir, orderValue = 'asc', search, searchValue, s
<Text id="global.orderDirDesc" />
</option>
</select>

<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
9 changes: 8 additions & 1 deletion front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1422,9 +1422,15 @@
}
},
"editScene": {
"settings": "Settings",
"nameTitle": "Scene name",
"editNamePlaceholder": "Enter a scene name",
"descriptionTitle": "Description",
"iconLabel": "Icon",
"editDescriptionPlaceholder": "Enter a scene description",
"tagsTitle": "Tags",
"editTagsPlaceholder": "Enter a scene tags",
"createTag": "Create tag: '{{tagName}}'",
"startButton": "Start",
"saveButton": "Save",
"deleteButton": "Delete",
Expand Down Expand Up @@ -2091,7 +2097,8 @@
"newButton": "New",
"editButton": "Edit",
"startButton": "Start",
"searchPlaceholder": "Search scenes"
"searchPlaceholder": "Search scenes",
"filterTagsName": "Filter by tags"
},
"gateway": {
"instanceConfiguredTitle": "Gladys Plus",
Expand Down
9 changes: 8 additions & 1 deletion front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1423,9 +1423,15 @@
}
},
"editScene": {
"settings": "Configuration",
"nameTitle": "Nom de scène",
"editNamePlaceholder": "Entrez un nom de scène",
"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",
"saveButton": "Sauvegarder",
"deleteButton": "Supprimer",
Expand Down Expand Up @@ -2092,7 +2098,8 @@
"newButton": "Nouveau",
"editButton": "Editer",
"startButton": "Démarrer",
"searchPlaceholder": "Chercher une scène"
"searchPlaceholder": "Chercher une scène",
"filterTagsName": "Filtrer par tags"
},
"gateway": {
"instanceConfiguredTitle": "Gladys Plus",
Expand Down
13 changes: 12 additions & 1 deletion front/src/routes/scene/SceneCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Component } from 'preact';
import { Link } from 'preact-router/match';
import cx from 'classnames';
import style from './style.css';
import { MAX_LENGTH_TAG } from './constant';

class SceneCard extends Component {
startScene = async () => {
Expand Down Expand Up @@ -34,7 +35,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 All @@ -53,6 +54,16 @@ class SceneCard extends Component {
</div>
<h4>{props.scene.name}</h4>
<div class={`text-muted ${style.descriptionSceneEllipsis}`}>{props.scene.description}</div>
<div>
{props.scene.tags &&
props.scene.tags.map(tag => (
<span class="badge badge-secondary mr-1">
{tag.name.length > MAX_LENGTH_TAG
? `${tag.name.substring(0, MAX_LENGTH_TAG - 3)}...`
: tag.name}
</span>
))}
</div>
</div>
<div class="mt-auto">
<div class="card-footer">
Expand Down
9 changes: 7 additions & 2 deletions front/src/routes/scene/ScenePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import CardFilter from '../../components/layout/CardFilter';
import SceneCards from './SceneCards';
import EmptyState from './EmptyState';
import style from './style.css';
import SceneTagFilter from './SceneTagFilter';

const ScenePage = ({ children, ...props }) => (
<div class="page">
Expand All @@ -26,8 +27,12 @@ const ScenePage = ({ children, ...props }) => (
searchPlaceHolder={<Text id="scene.searchPlaceholder" />}
/>
</Localizer>
<Link href="/dashboard/scene/new" class="btn btn-outline-primary ml-2">
<Text id="scene.newButton" /> <i class="fe fe-plus" />
<SceneTagFilter tags={props.tags} searchTags={props.searchTags} sceneTagSearch={props.sceneTagSearch} />
<Link href="/dashboard/scene/new" class={cx('btn', 'btn-outline-primary', 'ml-2', style.newButton)}>
<span class="d-none d-lg-inline-block mr-2">
<Text id="scene.newButton" />
</span>
<i class="fe fe-plus" />
</Link>
</div>
</div>
Expand Down
Loading
Loading