refactor create/settings controller to component (#820)

This commit is contained in:
Jeldrik Hanschke 2023-12-19 19:48:54 +01:00 committed by GitHub
parent f25625f40d
commit 27b36e2005
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 302 additions and 113 deletions

View file

@ -0,0 +1,113 @@
<div class="cr-form-wrapper box">
<BsForm
@formLayout="horizontal"
@model={{this}}
@onInvalid={{(scroll-first-invalid-element-into-view-port)}}
@onSubmit={{this.createPoll}}
novalidate
as |form|
>
<form.element
@label={{t "create.settings.answerType.label"}}
@property="answerType"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="answer-type"
as |el|
>
<select
id={{el.id}}
class="custom-select"
{{on "change" (pick "target.value" el.setValue)}}
{{autofocus}}
>
{{#each this.answerTypes as |answerType|}}
<option
value={{answerType.id}}
selected={{eq el.value answerType.id}}
>
{{t answerType.labelTranslation}}
</option>
{{/each}}
</select>
</form.element>
<form.element
@controlType="select"
@label={{t "create.settings.expirationDate.label"}}
@property="expirationDuration"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="expiration-duration"
as |el|
>
<select
id={{el.id}}
{{on "change" (pick "target.value" el.setValue)}}
class="custom-select"
>
{{#each this.expirationDurations as |duration|}}
<option value={{duration.id}} selected={{eq el.value duration.id}}>
{{t duration.labelTranslation}}
</option>
{{/each}}
</select>
</form.element>
<form.element
@controlType="checkbox"
@label={{t "create.settings.anonymousUser.label"}}
@showValidationOn="change"
@property="anonymousUser"
class="anonymous-user"
/>
<form.element
@controlType="checkbox"
@label={{t "create.settings.forceAnswer.label"}}
@showValidationOn="change"
@property="forceAnswer"
class="force-answer"
/>
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<SaveButton
@isPending={{form.isSubmitting}}
data-test-button="submit"
/>
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton @onClick={{this.previousPage}} />
</div>
</div>
<BsModal
@onHidden={{this.resetSavingPollFailedState}}
@onSubmit={{form.submit}}
@open={{this.savingPollFailed}}
data-test-modal="saving-failed"
as |modal|
>
<modal.header
@closeButton={{false}}
@title={{t "error.poll.savingFailed.title"}}
/>
<modal.body>
<p>
{{t "error.poll.savingFailed.description"}}
</p>
</modal.body>
<modal.footer>
<BsButton @onClick={{modal.close}} data-test-button="abort">
{{t "action.abort"}}
</BsButton>
<SaveButton
@isPending={{form.isSubmitting}}
@onClick={{modal.submit}}
data-test-button="retry"
type="button"
>
{{t "modal.save-retry.button-retry"}}
</SaveButton>
</modal.footer>
</BsModal>
</BsForm>
</div>

View file

@ -0,0 +1,188 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { isPresent } from '@ember/utils';
import { DateTime, Duration } from 'luxon';
import { generatePassphrase } from '../utils/encryption';
import Poll from '../models/poll';
import type IntlService from 'ember-intl/services/intl';
import type RouterService from '@ember/routing/router-service';
import type { CreateSettingsRouteModel } from 'croodle/routes/create/settings';
export interface CreateSettingsSignature {
Args: {
poll: CreateSettingsRouteModel;
};
}
export default class CreateSettingsComponent extends Component<CreateSettingsSignature> {
@service declare intl: IntlService;
@service declare router: RouterService;
@tracked savingPollFailed = false;
get anonymousUser() {
return this.args.poll.anonymousUser;
}
set anonymousUser(value) {
this.args.poll.anonymousUser = value;
}
get answerType() {
return this.args.poll.answerType;
}
set answerType(value) {
this.args.poll.answerType = value;
}
get answerTypes() {
return [
{ id: 'YesNo', labelTranslation: 'answerTypes.yesNo.label' },
{ id: 'YesNoMaybe', labelTranslation: 'answerTypes.yesNoMaybe.label' },
{ id: 'FreeText', labelTranslation: 'answerTypes.freeText.label' },
];
}
get expirationDuration() {
// TODO: must be calculated based on model.expirationDate
return 'P3M';
}
set expirationDuration(value) {
this.args.poll.expirationDate = isPresent(value)
? (DateTime.local().plus(Duration.fromISO(value)).toISO() as string)
: '';
}
get expirationDurations() {
return [
{
id: 'P7D',
labelTranslation: 'create.settings.expirationDurations.P7D',
},
{
id: 'P1M',
labelTranslation: 'create.settings.expirationDurations.P1M',
},
{
id: 'P3M',
labelTranslation: 'create.settings.expirationDurations.P3M',
},
{
id: 'P6M',
labelTranslation: 'create.settings.expirationDurations.P6M',
},
{
id: 'P1Y',
labelTranslation: 'create.settings.expirationDurations.P1Y',
},
{ id: '', labelTranslation: 'create.settings.expirationDurations.never' },
];
}
get forceAnswer() {
return this.args.poll.forceAnswer;
}
set forceAnswer(value) {
this.args.poll.forceAnswer = value;
}
@action
previousPage() {
const { pollType } = this.args.poll;
if (pollType === 'FindADate') {
this.router.transitionTo('create.options-datetime');
} else {
this.router.transitionTo('create.options');
}
}
@action
async createPoll() {
const { poll } = this.args;
const {
anonymousUser,
answerType,
description,
expirationDate,
forceAnswer,
freetextOptions,
dateOptions,
timesForDateOptions,
pollType,
title,
} = poll;
// calculate options
const options: string[] = [];
if (pollType === 'FindADate') {
// merge date with times
for (const date of dateOptions) {
if (timesForDateOptions.has(date)) {
for (const time of timesForDateOptions.get(date)!) {
const [hour, minute] = time.split(':') as [string, string];
options.push(
DateTime.fromISO(date)
.set({
hour: parseInt(hour),
minute: parseInt(minute),
second: 0,
millisecond: 0,
})
.toISO() as string,
);
}
} else {
options.push(date);
}
}
} else {
options.push(...freetextOptions);
}
// save poll
try {
const encryptionKey = generatePassphrase();
// save poll
const poll = await Poll.create(
{
anonymousUser,
answerType,
description,
expirationDate,
forceAnswer,
options: options.map((option) => {
return { title: option };
}),
pollType,
title,
},
encryptionKey,
);
// redirect to new poll
await this.router.transitionTo('poll.participation', poll.id, {
queryParams: {
encryptionKey,
},
});
} catch (err) {
this.savingPollFailed = true;
reportError(err);
}
}
@action
resetSavingPollFailedState() {
this.savingPollFailed = false;
}
}
declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
CreateSettings: typeof CreateSettingsComponent;
}
}

View file

@ -1,113 +1 @@
<div class="cr-form-wrapper box">
<BsForm
@formLayout="horizontal"
@model={{this}}
@onInvalid={{(scroll-first-invalid-element-into-view-port)}}
@onSubmit={{this.createPoll}}
novalidate
as |form|
>
<form.element
@label={{t "create.settings.answerType.label"}}
@property="answerType"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="answer-type"
as |el|
>
<select
id={{el.id}}
class="custom-select"
{{on "change" (pick "target.value" el.setValue)}}
{{autofocus}}
>
{{#each this.answerTypes as |answerType|}}
<option
value={{answerType.id}}
selected={{eq el.value answerType.id}}
>
{{t answerType.labelTranslation}}
</option>
{{/each}}
</select>
</form.element>
<form.element
@controlType="select"
@label={{t "create.settings.expirationDate.label"}}
@property="expirationDuration"
@showValidationOn={{array "change" "focusOut"}}
@useIcons={{false}}
class="expiration-duration"
as |el|
>
<select
id={{el.id}}
{{on "change" (pick "target.value" el.setValue)}}
class="custom-select"
>
{{#each this.expirationDurations as |duration|}}
<option value={{duration.id}} selected={{eq el.value duration.id}}>
{{t duration.labelTranslation}}
</option>
{{/each}}
</select>
</form.element>
<form.element
@controlType="checkbox"
@label={{t "create.settings.anonymousUser.label"}}
@showValidationOn="change"
@property="anonymousUser"
class="anonymous-user"
/>
<form.element
@controlType="checkbox"
@label={{t "create.settings.forceAnswer.label"}}
@showValidationOn="change"
@property="forceAnswer"
class="force-answer"
/>
<div class="row cr-steps-bottom-nav">
<div class="col-6 col-md-8 order-12">
<SaveButton
@isPending={{form.isSubmitting}}
data-test-button="submit"
/>
</div>
<div class="col-6 col-md-4 order-1 text-right">
<BackButton @onClick={{this.previousPage}} />
</div>
</div>
<BsModal
@onHidden={{this.resetSavingPollFailedState}}
@onSubmit={{form.submit}}
@open={{this.savingPollFailed}}
data-test-modal="saving-failed"
as |modal|
>
<modal.header
@closeButton={{false}}
@title={{t "error.poll.savingFailed.title"}}
/>
<modal.body>
<p>
{{t "error.poll.savingFailed.description"}}
</p>
</modal.body>
<modal.footer>
<BsButton @onClick={{modal.close}} data-test-button="abort">
{{t "action.abort"}}
</BsButton>
<SaveButton
@isPending={{form.isSubmitting}}
@onClick={{modal.submit}}
data-test-button="retry"
type="button"
>
{{t "modal.save-retry.button-retry"}}
</SaveButton>
</modal.footer>
</BsModal>
</BsForm>
</div>
<CreateSettings @poll={{@model}} />