Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 11 additions & 6 deletions app/components/school/new-vocabulary-form.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TrackedAsyncData } from 'ember-async-data';
import { uniqueId } from '@ember/helper';
import t from 'ember-intl/helpers/t';
import { on } from '@ember/modifier';
import focus from '../../modifiers/focus';
import pick from '../../helpers/pick';
import set from 'ember-set-helper/helpers/set';
import perform from 'ember-concurrency/helpers/perform';
Expand All @@ -16,7 +17,6 @@ import LoadingSpinner from '../loading-spinner';

export default class SchoolNewVocabularyFormComponent extends Component {
@service intl;
@service store;
@tracked title;

validations = new YupValidations(this, {
Expand Down Expand Up @@ -47,20 +47,20 @@ export default class SchoolNewVocabularyFormComponent extends Component {
return this.schoolVocabulariesData.value.map(({ title }) => title);
}

saveNew = task({ drop: true }, async () => {
save = task({ drop: true }, async () => {
this.validations.addErrorDisplayForAllFields();
const isValid = await this.validations.isValid();
if (!isValid) {
return false;
}
await this.args.save.linked().perform(this.title, this.args.school, true);
await this.args.save(this.title, this.args.school, true);
this.validations.clearErrorDisplay();
});

saveOrCancel = task({ drop: true }, async (event) => {
const keyCode = event.keyCode;
if (13 === keyCode) {
await this.saveNew.perform();
await this.save.perform();
return;
}
if (27 === keyCode) {
Expand All @@ -70,6 +70,9 @@ export default class SchoolNewVocabularyFormComponent extends Component {
<template>
{{#let (uniqueId) as |templateId|}}
<div class="school-new-vocabulary-form" data-test-school-new-vocabulary-form ...attributes>
<div class="title">
<h3>{{t "general.newVocabulary"}}</h3>
</div>
<div class="form">
<div class="item" data-test-title>
<label for="title-{{templateId}}">
Expand All @@ -79,6 +82,8 @@ export default class SchoolNewVocabularyFormComponent extends Component {
id="title-{{templateId}}"
type="text"
value={{this.title}}
placeholder={{t "general.vocabularyPlaceholder"}}
{{focus}}
{{on "input" (pick "target.value" (set this "title"))}}
{{on "keyup" (perform this.saveOrCancel)}}
{{this.validations.attach "title"}}
Expand All @@ -94,9 +99,9 @@ export default class SchoolNewVocabularyFormComponent extends Component {
type="button"
class="done text"
data-test-submit
{{on "click" (perform this.saveNew)}}
{{on "click" (perform this.save)}}
>
{{#if this.saveNew.isRunning}}
{{#if this.save.isRunning}}
<LoadingSpinner />
{{else}}
{{t "general.done"}}
Expand Down
38 changes: 5 additions & 33 deletions app/components/school/vocabularies-expanded.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import Component from '@glimmer/component';
import { cached } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { task } from 'ember-concurrency';
import { findById } from '../../utils/array-helpers';
import { TrackedAsyncData } from 'ember-async-data';
import { on } from '@ember/modifier';
import t from 'ember-intl/helpers/t';
Expand All @@ -12,7 +10,6 @@ import { and, not } from 'ember-truth-helpers';
import ExpandCollapseButton from '../expand-collapse-button';
import { fn } from '@ember/helper';
import NewVocabularyForm from './new-vocabulary-form';
import VocabularyTermManager from './vocabulary-term-manager';
import VocabularyManager from './vocabulary-manager';
import VocabulariesList from './vocabularies-list';
import LoadingSpinner from '../loading-spinner';
Expand Down Expand Up @@ -73,22 +70,6 @@ export default class SchoolVocabulariesExpandedComponent extends Component {
return vocabulary;
}

get managedTerm() {
if (
!this.schoolVocabularies.length ||
!this.args.managedVocabularyId ||
!this.args.managedTermId
) {
return null;
}

const { terms } = this.schoolVocabularies.find(({ vocabulary }) => {
return Number(this.args.managedVocabularyId) === Number(vocabulary.id);
});

return findById(terms, this.args.managedTermId);
}

@action
doCollapse() {
if (this.isCollapsible && this.schoolVocabularies.length) {
Expand All @@ -98,15 +79,15 @@ export default class SchoolVocabulariesExpandedComponent extends Component {
}
}

saveNewVocabulary = task({ drop: true }, async (title, school, active) => {
const vocabulary = this.store.createRecord('vocabulary', {
saveNewVocabulary = async (title, school, active) => {
const vocabulary = await this.store.createRecord('vocabulary', {
title,
school,
active,
});
this.args.setSchoolNewVocabulary(null);
await vocabulary.save();
});
};
<template>
<section
class="school-vocabularies-expanded"
Expand Down Expand Up @@ -153,23 +134,14 @@ export default class SchoolVocabulariesExpandedComponent extends Component {
@save={{this.saveNewVocabulary}}
/>
{{/if}}
{{#if this.managedTerm}}
<VocabularyTermManager
@vocabulary={{this.managedVocabulary}}
@term={{this.managedTerm}}
@manageTerm={{@setSchoolManagedVocabularyTerm}}
@manageVocabulary={{@setSchoolManagedVocabulary}}
@canUpdate={{@canUpdateTerm}}
@canDelete={{@canDeleteTerm}}
@canCreate={{@canCreateTerm}}
/>
{{else if this.managedVocabulary}}
{{#if this.managedVocabulary}}
<VocabularyManager
@vocabulary={{this.managedVocabulary}}
@manageTerm={{@setSchoolManagedVocabularyTerm}}
@manageVocabulary={{@setSchoolManagedVocabulary}}
@canUpdate={{@canUpdateVocabulary}}
@canCreate={{@canCreateTerm}}
@canDelete={{@canDeleteTerm}}
/>
{{else}}
<VocabulariesList
Expand Down
200 changes: 200 additions & 0 deletions app/components/school/vocabulary-edit-term.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { task } from 'ember-concurrency';
import YupValidations from '../../classes/yup-validations';
import { string } from 'yup';
import t from 'ember-intl/helpers/t';
import { on } from '@ember/modifier';
import { uniqueId } from '@ember/helper';
import { and, not } from 'ember-truth-helpers';
import pick from '../../helpers/pick';
import set from 'ember-set-helper/helpers/set';
import perform from 'ember-concurrency/helpers/perform';
import focus from '../../modifiers/focus';
import LoadingSpinner from '../loading-spinner';
import YupValidationMessage from '../yup-validation-message';
import ToggleYesno from '../toggle-yesno';
import FaIcon from '@fortawesome/ember-fontawesome/components/fa-icon';
import { faPlus, faTrash } from '@fortawesome/free-solid-svg-icons';

export default class SchoolVocabularyEditTermComponent extends Component {
@tracked titleBuffer;
@tracked descriptionBuffer;
@tracked isActiveBuffer;

validations = new YupValidations(this, {
title: string()
.ensure()
.trim()
.required()
.max(200)
.test(
'term-title-uniqueness',
(d) => ({ path: d.path, messageKey: 'errors.exclusion' }),
async (value) => {
let terms;
if (this.args.term.isTopLevel) {
const vocab = await this.args.term.vocabulary;
terms = await vocab.getTopLevelTerms();
} else {
const parent = await this.args.term.parent;
terms = await parent.children;
}
return !terms.filter((term) => term.id !== this.args.term.id && term.title === value)
.length;
},
),
});

get title() {
return this.titleBuffer ?? this.args.term.title;
}

get isActive() {
return this.isActiveBuffer ?? this.args.term.active;
}

get description() {
return this.descriptionBuffer ?? this.args.term.description;
}

save = task({ drop: true }, async () => {
this.validations.addErrorDisplayForAllFields();
const isValid = await this.validations.isValid();
if (!isValid) {
return false;
}
this.validations.clearErrorDisplay();
this.args.term.title = this.title;
this.args.term.active = this.isActive;
this.args.term.description = this.description;
await this.args.term.save();
this.args.cancel();
});

<template>
{{#let (uniqueId) as |templateId|}}
<div class="school-vocabulary-edit-term" data-test-school-vocabulary-edit-term>
<div class="header">
<div class="title" data-test-vocabulary-edit-term-title>
<h3>
{{#if @term.isTopLevel}}
{{t "general.editTerm"}}
{{else}}
{{t "general.editSubTerm"}}
{{/if}}
</h3>
</div>

<span class="actions">
{{#if @canCreate}}
<button
class="link-button add-button"
type="button"
data-test-add-sub-term
title={{t "general.addSubTerm"}}
{{on "click" @toggleAddSubTermForm}}
>
<FaIcon @icon={{faPlus}} class="enabled" />
</button>
{{/if}}
{{#if @canDelete}}
{{#if (and (not @term.hasChildren) (not @term.hasAssociations))}}
<button
class="link-button delete-button{{if @termDeleteDisabled ' disabled'}}"
type="button"
disabled={{@termDeleteDisabled}}
data-test-delete
title={{if
@showRemovalConfirmation
(t "general.disabledByConfirmation")
(t "general.remove")
}}
{{on "click" @confirmRemoval}}
>
<FaIcon
@icon={{faTrash}}
class={{if @termDeleteDisabled "disabled" "enabled remove"}}
/>
</button>
{{else}}
<button
type="button"
class="link-button delete-button disabled"
title={{t "general.canNotDeleteSchoolVocabularyTerm"}}
disabled
data-test-delete
>
<FaIcon @icon={{faTrash}} class="disabled" />
</button>
{{/if}}
{{/if}}
</span>
</div>

<div class="form">
<div class="item" data-test-vocabulary-term-title>
<label for="title-{{templateId}}">
{{t "general.title"}}:
</label>
<input
id="title-{{templateId}}"
type="text"
value={{this.title}}
disabled={{this.save.isRunning}}
{{focus}}
{{on "input" (pick "target.value" (set this "titleBuffer"))}}
{{this.validations.attach "title"}}
/>
<YupValidationMessage
@description={{t "general.term"}}
@validationErrors={{this.validations.errors.title}}
data-test-title-validation-error-message
/>
</div>

<div class="item" data-test-vocabulary-term-is-active>
<label>
{{t "general.active"}}:
</label>
<ToggleYesno
@yes={{this.isActive}}
@toggle={{set this "isActiveBuffer"}}
@disabled={{this.save.isRunning}}
/>
</div>

<div class="item" data-test-vocabulary-term-description>
<label for="description-{{templateId}}">
{{t "general.description"}}:
</label>
<textarea
id="description-{{templateId}}"
disabled={{this.save.isRunning}}
{{on "input" (pick "target.value" (set this "descriptionBuffer"))}}
>{{this.description}}</textarea>
</div>

<div class="buttons">
<button
type="button"
class="done text"
disabled={{this.save.isRunning}}
data-test-submit
{{on "click" (perform this.save)}}
>
{{#if this.save.isRunning}}
<LoadingSpinner />
{{else}}
{{t "general.save"}}
{{/if}}
</button>
<button type="button" class="cancel text" {{on "click" @cancel}} data-test-cancel>
{{t "general.cancel"}}
</button>
</div>
</div>
</div>
{{/let}}
</template>
}
Loading
Loading