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

FEATURE: add Optional countdown for e.g. deadlines #149

Merged
merged 31 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b0da77d
convert Event Form Component to Glimmer gjs
merefield Sep 4, 2024
ad71739
FEATURE: add deadline event option behind setting
merefield Sep 4, 2024
3186301
linting
merefield Sep 4, 2024
b9590ba
first working
merefield Sep 8, 2024
de3a3d1
add deadline attribute migration
merefield Sep 8, 2024
3ca6fc8
add different icons for passed due or in progress
merefield Sep 8, 2024
50dfef5
clean up
merefield Sep 8, 2024
4b42ff7
add past due format
merefield Sep 9, 2024
d6b6cbb
add tests, fix dependency
merefield Sep 18, 2024
309a4cc
merge in main
merefield Sep 18, 2024
324fa2c
linting
merefield Sep 18, 2024
074dc01
linting
merefield Sep 18, 2024
f98ac14
linting
merefield Sep 18, 2024
3fb31f5
test linting
merefield Sep 18, 2024
06f1b48
more test linting
merefield Sep 18, 2024
496dd1b
linting
merefield Sep 18, 2024
e4fa675
linting
merefield Sep 18, 2024
1e70108
linting
merefield Sep 18, 2024
e238b01
fix merge error
merefield Sep 18, 2024
c60a659
remove redundant decorator
merefield Sep 18, 2024
f82eb83
rename settings
merefield Sep 18, 2024
7b00498
delete unnecessary migration
merefield Sep 18, 2024
9ba5e17
linting
merefield Sep 18, 2024
10246da
Remove deadline from serializer
angusmcleod Sep 18, 2024
46aaf3a
improve formatting of countdown: use core localisation, suppress zeros
merefield Sep 18, 2024
68c3674
linting
merefield Sep 18, 2024
8771d31
add space before countdown icon
merefield Sep 18, 2024
d98a81c
fix timezone update
merefield Sep 19, 2024
00011b0
tweak margins of hourglass icon
merefield Sep 19, 2024
090f97a
improve events calendar card presentation
merefield Sep 19, 2024
dca047c
make icon margin consistent for deadline icons on event card
merefield Sep 19, 2024
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
1 change: 1 addition & 0 deletions app/serializers/discourse_events/basic_event_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class BasicEventSerializer < ApplicationSerializer
attributes :id,
:start_time,
:end_time,
:deadline,
:name,
:description,
:status,
Expand Down
319 changes: 319 additions & 0 deletions assets/javascripts/discourse/components/event-form.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { hash } from "rsvp";
import DateInput from "discourse/components/date-input";
import InputTip from "discourse/components/input-tip";
import TimeInput from "discourse/components/time-input";
import concatClass from "discourse/helpers/concat-class";
import i18n from "discourse-common/helpers/i18n";
import ComboBox from "select-kit/components/combo-box";
import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser";
import {
compileEvent,
nextInterval,
setupEventForm,
timezoneLabel,
} from "../lib/date-utilities";

export default class EventForm extends Component {
@service siteSettings;
@service site;
@tracked endEnabled = false;
@tracked allDay = false;
@tracked deadline = false;
@tracked showTimezone = false;
@tracked startDate;
@tracked startTime;
@tracked endDate;
@tracked endTime;
@tracked timezone;
@tracked rsvpEnabled = false;
@tracked goingMax;
@tracked usersGoing;

constructor() {
super(...arguments);
this.setupProperties();
}

@action
setupProperties() {
merefield marked this conversation as resolved.
Show resolved Hide resolved
const props = setupEventForm(this.args.event, {
siteSettings: this.siteSettings,
});
Object.assign(this, props);
if (
this.siteSettings.events_add_default_end_time &&
!this.args.event &&
!this.endDate &&
!this.endTime
) {
this.endEnabled = true;
}
if (this.siteSettings.events_support_deadlines) {
this.toggleDeadlineEnabled;
}
}

get timezones() {
const eventTimezones =
this.args.event?.eventTimezones || this.site.event_timezones;
return eventTimezones.map((tz) => ({
value: tz.value,
name: timezoneLabel(tz.value, { siteSettings: this.siteSettings }),
}));
}

get showDeadlineToggle() {
return this.siteSettings.events_support_deadlines;
}

@action
onChangeStartDate(date) {
this.startDate = moment(date);
this.updateEvent();
}

@action
onChangeEndDate(date) {
this.endDate = moment(date);
this.updateEvent();
}

@action
onChangeStartTime(time) {
this.startTime = moment(time);
this.updateEvent();
}

@action
onChangeEndTime(time) {
this.endTime = moment(time);
this.updateEvent();
}

@action
toggleEndEnabled(event) {
this.endEnabled = event.target.checked;
if (this.endEnabled) {
if (!this.endDate) {
this.endDate = this.startDate;
}
if (!this.allDay && !this.endTime) {
const start = moment(
`${moment(this.startDate).format(
"YYYY-MM-DD"
)} ${this.startTime.format("HH:mm")}`
);
this.endTime = moment(start).add(1, "hours");
}
} else {
this.endDate = undefined;
this.endTime = undefined;
}
this.updateEvent();
}

@action
toggleAllDay(event) {
this.allDay = event.target.checked;
if (!this.allDay) {
const start = nextInterval();
this.startTime = start;
if (this.endEnabled) {
this.endTime = moment(start).add(1, "hours");
}
}
this.updateEvent();
}

@action
toggleDeadline(event) {
this.deadline = event.target.checked;
this.updateEvent();
}

@action
updateEvent() {
const event = compileEvent({
startDate: this.startDate,
startTime: this.startTime,
endDate: this.endDate,
endTime: this.endTime,
endEnabled: this.endEnabled,
allDay: this.allDay,
deadline: this.deadline,
timezone: this.timezone,
rsvpEnabled: this.rsvpEnabled,
goingMax: this.goingMax,
usersGoing: this.usersGoing,
});
this.args.updateEvent(event, this.eventValid(event));
}

@action
eventValid(event) {
return !event || !event.end || moment(event.end).isSameOrAfter(event.start);
}
<template>
<div class="event-form">
<div class="event-controls">
<div class="control">
<Input
@type="checkbox"
@checked={{this.endEnabled}}
{{on "change" this.toggleEndEnabled}}
/>
<span>{{i18n "add_event.end_enabled"}}</span>
</div>

<div class="control">
<Input
@type="checkbox"
@checked={{this.allDay}}
{{on "change" this.toggleAllDay}}
/>
<span>{{i18n "add_event.all_day"}}</span>
</div>

{{#if this.showDeadlineToggle}}
<div class="control deadline">
<Input
@type="checkbox"
@checked={{this.deadline}}
title={{i18n "add_event.deadline.title"}}
{{on "click" this.toggleDeadline}}
/>
<span title={{i18n "add_event.deadline.title"}}>{{i18n
"add_event.deadline.label"
}}</span>
</div>
{{/if}}

{{#unless this.allDay}}
<div class="control full-width">
<ComboBox
@id="add-event-select-timezone"
@value={{this.timezone}}
@valueProperty="value"
@onChange={{mut this.timezone}}
@content={{this.timezones}}
@options={{hash filterable=true none="add_event.no_timezone"}}
/>
</div>
{{/unless}}
</div>

<div class="datetime-controls">
<div class="start-card date-time-card">
<span class="sub-title">
{{i18n "add_event.event_start"}}
</span>

<InputTip @validation={{this.startDateTimeValidation}} />

<div class="date-time-set">
<div class="date-area">
<label class="input-group-label">
{{i18n "add_event.event_date"}}
</label>

<DateInput
@date={{this.startDate}}
@onChange={{this.onChangeStartDate}}
@useGlobalPickerContainer={{true}}
/>
</div>

{{#unless this.allDay}}
<div class="time-area">
<label class="input-group-label">
{{i18n "add_event.event_time"}}
</label>

<TimeInput
@date={{this.startTime}}
@onChange={{this.onChangeStartTime}}
/>
</div>
{{/unless}}
</div>
</div>

<div
class={{concatClass
"end-card date-time-card"
(unless this.endEnabled "disabled")
}}
>
<span class="sub-title">
{{i18n "add_event.event_end"}}
</span>

<InputTip @validation={{this.scheduleDateTimeValidation}} />

<div class="date-time-set">
<div class="date-area">
<label class="input-group-label">
{{i18n "add_event.event_date"}}
</label>

<DateInput
@date={{this.endDate}}
@onChange={{this.onChangeEndDate}}
@useGlobalPickerContainer={{true}}
/>
</div>

{{#unless this.allDay}}
<div class="time-area">
<label class="input-group-label">
{{i18n "add_event.event_time"}}
</label>

<TimeInput
@date={{this.endTime}}
@onChange={{this.onChangeEndTime}}
/>
</div>
{{/unless}}
</div>
</div>
</div>

{{#if this.siteSettings.events_rsvp}}
<div class="rsvp-controls">
<div class="control">
<Input @type="checkbox" @checked={{this.rsvpEnabled}} />
<span>{{i18n "add_event.rsvp_enabled"}}</span>
</div>

{{#if this.rsvpEnabled}}
<div class="rsvp-container">
<div class="control">
<span>{{i18n "add_event.going_max"}}</span>
<Input @type="number" @value={{this.goingMax}} />
</div>

<div class="control full-width">
<span>{{i18n "add_event.going"}}</span>
<EmailGroupUserChooser
@value={{this.usersGoing}}
@onChange={{action (mut this.usersGoing)}}
class="user-selector"
@options={{hash
filterPlaceholder="composer.users_placeholder"
}}
/>
</div>
</div>
{{/if}}
</div>
{{/if}}
</div>
</template>
}
Loading
Loading