FEATURE: upcoming events list component (#463)
* FEATURE: upcoming events list component
This commit is contained in:
parent
b8a8a241c6
commit
f57b09ce8b
|
@ -0,0 +1,178 @@
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import I18n from "discourse-i18n";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { isNotFullDayEvent } from "../lib/guess-best-date-format";
|
||||||
|
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||||
|
import or from "truth-helpers/helpers/or";
|
||||||
|
|
||||||
|
export const DEFAULT_MONTH_FORMAT = "MMMM YYYY";
|
||||||
|
export const DEFAULT_DATE_FORMAT = "dddd, MMM D";
|
||||||
|
export const DEFAULT_TIME_FORMAT = "LT";
|
||||||
|
|
||||||
|
export default class UpcomingEventsList extends Component {
|
||||||
|
@service appEvents;
|
||||||
|
@service siteSettings;
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
@tracked isLoading = true;
|
||||||
|
@tracked hasError = false;
|
||||||
|
@tracked eventsByMonth = {};
|
||||||
|
|
||||||
|
monthFormat = this.args.params?.monthFormat ?? DEFAULT_MONTH_FORMAT;
|
||||||
|
dateFormat = this.args.params?.dateFormat ?? DEFAULT_DATE_FORMAT;
|
||||||
|
timeFormat = this.args.params?.timeFormat ?? DEFAULT_TIME_FORMAT;
|
||||||
|
|
||||||
|
title = I18n.t("discourse_post_event.upcoming_events_list.title");
|
||||||
|
emptyMessage = I18n.t("discourse_post_event.upcoming_events_list.empty");
|
||||||
|
allDayLabel = I18n.t("discourse_post_event.upcoming_events_list.all_day");
|
||||||
|
errorMessage = I18n.t("discourse_post_event.upcoming_events_list.error");
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.appEvents.on("page:changed", this, this.updateEventsByMonth);
|
||||||
|
}
|
||||||
|
|
||||||
|
get shouldRender() {
|
||||||
|
if (!this.categoryId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventSettings =
|
||||||
|
this.siteSettings.events_calendar_categories.split("|");
|
||||||
|
|
||||||
|
return eventSettings.includes(this.categoryId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
get categoryId() {
|
||||||
|
return this.router.currentRoute.attributes?.category?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasEmptyResponse() {
|
||||||
|
return (
|
||||||
|
!this.isLoading &&
|
||||||
|
!this.hasError &&
|
||||||
|
Object.keys(this.eventsByMonth).length === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async updateEventsByMonth() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.hasError = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { events } = await ajax("/discourse-post-event/events", {
|
||||||
|
data: { category_id: this.categoryId },
|
||||||
|
});
|
||||||
|
|
||||||
|
this.eventsByMonth = this.groupByMonthAndDay(events);
|
||||||
|
} catch {
|
||||||
|
this.hasError = true;
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
formatMonth(month) {
|
||||||
|
return moment(month, "YYYY-MM").format(this.monthFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
formatDate(month, day) {
|
||||||
|
return moment(`${month}-${day}`, "YYYY-MM-DD").format(this.dateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
formatTime({ starts_at, ends_at }) {
|
||||||
|
return isNotFullDayEvent(moment(starts_at), moment(ends_at))
|
||||||
|
? moment(starts_at).format(this.timeFormat)
|
||||||
|
: this.allDayLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupByMonthAndDay(data) {
|
||||||
|
return data.reduce((result, item) => {
|
||||||
|
const date = new Date(item.starts_at);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const day = date.getDate();
|
||||||
|
|
||||||
|
const monthKey = `${year}-${month}`;
|
||||||
|
|
||||||
|
result[monthKey] = result[monthKey] ?? {};
|
||||||
|
result[monthKey][day] = result[monthKey][day] ?? [];
|
||||||
|
|
||||||
|
result[monthKey][day].push(item);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.shouldRender}}
|
||||||
|
<div class="upcoming-events-list">
|
||||||
|
<h3 class="upcoming-events-list__heading">
|
||||||
|
{{this.title}}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="upcoming-events-list__container">
|
||||||
|
<ConditionalLoadingSpinner @condition={{this.isLoading}} />
|
||||||
|
|
||||||
|
{{#if this.hasEmptyResponse}}
|
||||||
|
<div class="upcoming-events-list__empty-message">
|
||||||
|
{{this.emptyMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.hasError}}
|
||||||
|
<div class="upcoming-events-list__error-message">
|
||||||
|
{{this.errorMessage}}
|
||||||
|
</div>
|
||||||
|
<DButton
|
||||||
|
@action={{this.updateEventsByMonth}}
|
||||||
|
@label="discourse_post_event.upcoming_events_list.try_again"
|
||||||
|
class="btn-link upcoming-events-list__try-again"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless this.isLoading}}
|
||||||
|
{{#each-in this.eventsByMonth as |month monthData|}}
|
||||||
|
{{#if this.monthFormat}}
|
||||||
|
<h4 class="upcoming-events-list__formatted-month">
|
||||||
|
{{this.formatMonth month}}
|
||||||
|
</h4>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#each-in monthData as |day events|}}
|
||||||
|
<div class="upcoming-events-list__day-section">
|
||||||
|
<div class="upcoming-events-list__formatted-day">
|
||||||
|
{{this.formatDate month day}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#each events as |event|}}
|
||||||
|
<a
|
||||||
|
class="upcoming-events-list__event"
|
||||||
|
href={{event.post.url}}
|
||||||
|
>
|
||||||
|
<div class="upcoming-events-list__event-time">
|
||||||
|
{{this.formatTime event}}
|
||||||
|
</div>
|
||||||
|
<div class="upcoming-events-list__event-name">
|
||||||
|
{{or event.name event.post.topic.title}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/each-in}}
|
||||||
|
{{/each-in}}
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -63,6 +63,7 @@
|
||||||
background: var(--tertiary);
|
background: var(--tertiary);
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
margin: 0.3em 0 0.3em 0.5em;
|
margin: 0.3em 0 0.3em 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +123,7 @@
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fc-list-item-add-to-calendar {
|
.fc-list-item-add-to-calendar {
|
||||||
color: var(--tertiary);
|
color: var(--tertiary);
|
||||||
font-size: var(--font-down-1);
|
font-size: var(--font-down-1);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
.upcoming-events-list {
|
||||||
|
&__formatted-month {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__day-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__formatted-day {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: var(--base-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__event {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 1rem;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
line-height: var(--line-height-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__event-name {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__event-time {
|
||||||
|
width: 30%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -441,6 +441,12 @@ en:
|
||||||
creator: "Creator"
|
creator: "Creator"
|
||||||
status: "Status"
|
status: "Status"
|
||||||
starts_at: "Starts at"
|
starts_at: "Starts at"
|
||||||
|
upcoming_events_list:
|
||||||
|
title: "Upcoming events"
|
||||||
|
empty: "No upcoming events"
|
||||||
|
all_day: "All-day"
|
||||||
|
error: "Failed to retrieve events"
|
||||||
|
try_again: "Try again"
|
||||||
category:
|
category:
|
||||||
sort_topics_by_event_start_date: "Sort topics by event start date."
|
sort_topics_by_event_start_date: "Sort topics by event start date."
|
||||||
disable_topic_resorting: "Disable topic resorting."
|
disable_topic_resorting: "Disable topic resorting."
|
||||||
|
|
|
@ -32,6 +32,7 @@ register_asset "stylesheets/mobile/discourse-post-event.scss", :mobile
|
||||||
register_asset "stylesheets/desktop/discourse-calendar.scss", :desktop
|
register_asset "stylesheets/desktop/discourse-calendar.scss", :desktop
|
||||||
register_asset "stylesheets/colors.scss", :color_definitions
|
register_asset "stylesheets/colors.scss", :color_definitions
|
||||||
register_asset "stylesheets/common/user-preferences.scss"
|
register_asset "stylesheets/common/user-preferences.scss"
|
||||||
|
register_asset "stylesheets/common/upcoming-events-list.scss"
|
||||||
register_svg_icon "fas fa-calendar-day"
|
register_svg_icon "fas fa-calendar-day"
|
||||||
register_svg_icon "fas fa-clock"
|
register_svg_icon "fas fa-clock"
|
||||||
register_svg_icon "fas fa-file-csv"
|
register_svg_icon "fas fa-file-csv"
|
||||||
|
|
|
@ -0,0 +1,309 @@
|
||||||
|
import { module, test } from "qunit";
|
||||||
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||||
|
import { render, waitFor } from "@ember/test-helpers";
|
||||||
|
import UpcomingEventsList, {
|
||||||
|
DEFAULT_MONTH_FORMAT,
|
||||||
|
DEFAULT_DATE_FORMAT,
|
||||||
|
DEFAULT_TIME_FORMAT,
|
||||||
|
} from "../../discourse/components/upcoming-events-list";
|
||||||
|
import Service from "@ember/service";
|
||||||
|
import pretender, { response } from "discourse/tests/helpers/create-pretender";
|
||||||
|
import {
|
||||||
|
query,
|
||||||
|
queryAll,
|
||||||
|
fakeTime,
|
||||||
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import { hash } from "@ember/helper";
|
||||||
|
|
||||||
|
class RouterStub extends Service {
|
||||||
|
currentRoute = { attributes: { category: { id: 1 } } };
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = "2100-02-01T08:00:00";
|
||||||
|
const tomorrowAllDay = "2100-02-02T00:00:00";
|
||||||
|
const nextMonth = "2100-03-02T08:00:00";
|
||||||
|
|
||||||
|
module("Integration | Component | upcoming-events-list", function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
let clock;
|
||||||
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
this.owner.unregister("service:router");
|
||||||
|
this.owner.register("service:router", RouterStub);
|
||||||
|
|
||||||
|
this.siteSettings.events_calendar_categories = "1";
|
||||||
|
|
||||||
|
this.appEvents = this.container.lookup("service:appEvents");
|
||||||
|
|
||||||
|
clock = fakeTime(today, null, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
hooks.afterEach(() => {
|
||||||
|
clock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("empty state message", async function (assert) {
|
||||||
|
pretender.get("/discourse-post-event/events", () => {
|
||||||
|
return response({ events: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(<template><UpcomingEventsList /></template>);
|
||||||
|
|
||||||
|
this.appEvents.trigger("page:changed", {});
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__heading").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.title"),
|
||||||
|
"it displays the title"
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(".loading-container .spinner", { count: 0 });
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__empty-message").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.empty"),
|
||||||
|
"it displays the empty list message"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("with events, standard formats", async function (assert) {
|
||||||
|
pretender.get("/discourse-post-event/events", twoEventsResponseHandler);
|
||||||
|
|
||||||
|
await render(<template><UpcomingEventsList /></template>);
|
||||||
|
|
||||||
|
this.appEvents.trigger("page:changed", {});
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__heading").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.title"),
|
||||||
|
"it displays the title"
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(".loading-container .spinner", { count: 0 });
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__formatted-month")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
[
|
||||||
|
moment(tomorrowAllDay).format(DEFAULT_MONTH_FORMAT),
|
||||||
|
moment(nextMonth).format(DEFAULT_MONTH_FORMAT),
|
||||||
|
],
|
||||||
|
"it displays the formatted month"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__formatted-day")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
[
|
||||||
|
moment(tomorrowAllDay).format(DEFAULT_DATE_FORMAT),
|
||||||
|
moment(nextMonth).format(DEFAULT_DATE_FORMAT),
|
||||||
|
],
|
||||||
|
"it displays the formatted day"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__event-time")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
[
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.all_day"),
|
||||||
|
moment(nextMonth).format(DEFAULT_TIME_FORMAT),
|
||||||
|
],
|
||||||
|
"it displays the formatted time"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__event-name")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
["Awesome Event", "Another Awesome Event"],
|
||||||
|
"it displays the event name"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("with events, overriden formats", async function (assert) {
|
||||||
|
pretender.get("/discourse-post-event/events", twoEventsResponseHandler);
|
||||||
|
|
||||||
|
await render(<template>
|
||||||
|
<UpcomingEventsList
|
||||||
|
@params={{hash monthFormat="" dateFormat="L" timeFormat="LLL"}}
|
||||||
|
/>
|
||||||
|
</template>);
|
||||||
|
|
||||||
|
this.appEvents.trigger("page:changed", {});
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__heading").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.title"),
|
||||||
|
"it displays the title"
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(".loading-container .spinner", { count: 0 });
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
!exists(".upcoming-events-list__formatted-month"),
|
||||||
|
"it omits the formatted month when empty"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__formatted-day")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
[moment(tomorrowAllDay).format("L"), moment(nextMonth).format("L")],
|
||||||
|
"it displays the formatted day"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__event-time")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
[
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.all_day"),
|
||||||
|
moment(nextMonth).format("LLL"),
|
||||||
|
],
|
||||||
|
"it displays the formatted time"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...queryAll(".upcoming-events-list__event-name")].map(
|
||||||
|
(el) => el.innerText
|
||||||
|
),
|
||||||
|
["Awesome Event", "Another Awesome Event"],
|
||||||
|
"it displays the event name"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("with an error response", async function (assert) {
|
||||||
|
pretender.get("/discourse-post-event/events", () => {
|
||||||
|
return response(500, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(<template><UpcomingEventsList /></template>);
|
||||||
|
|
||||||
|
this.appEvents.trigger("page:changed", {});
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__heading").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.title"),
|
||||||
|
"it displays the title"
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(".loading-container .spinner", { count: 0 });
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__error-message").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.error"),
|
||||||
|
"it displays the error message"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".upcoming-events-list__try-again").innerText,
|
||||||
|
I18n.t("discourse_post_event.upcoming_events_list.try_again"),
|
||||||
|
"it displays the try again button"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function twoEventsResponseHandler() {
|
||||||
|
return response({
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
id: 67501,
|
||||||
|
creator: {
|
||||||
|
id: 1500588,
|
||||||
|
username: "foobar",
|
||||||
|
name: null,
|
||||||
|
avatar_template: "/user_avatar/localhost/foobar/{size}/1913_2.png",
|
||||||
|
assign_icon: "user-plus",
|
||||||
|
assign_path: "/u/foobar/activity/assigned",
|
||||||
|
},
|
||||||
|
sample_invitees: [],
|
||||||
|
watching_invitee: null,
|
||||||
|
starts_at: tomorrowAllDay,
|
||||||
|
ends_at: null,
|
||||||
|
timezone: "Asia/Calcutta",
|
||||||
|
stats: {
|
||||||
|
going: 0,
|
||||||
|
interested: 0,
|
||||||
|
not_going: 0,
|
||||||
|
invited: 0,
|
||||||
|
},
|
||||||
|
status: "public",
|
||||||
|
raw_invitees: ["trust_level_0"],
|
||||||
|
post: {
|
||||||
|
id: 67501,
|
||||||
|
post_number: 1,
|
||||||
|
url: "/t/this-is-an-event/18449/1",
|
||||||
|
topic: {
|
||||||
|
id: 18449,
|
||||||
|
title: "This is an event",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: "Awesome Event",
|
||||||
|
can_act_on_discourse_post_event: true,
|
||||||
|
can_update_attendance: true,
|
||||||
|
is_expired: false,
|
||||||
|
is_ongoing: true,
|
||||||
|
should_display_invitees: false,
|
||||||
|
url: null,
|
||||||
|
custom_fields: {},
|
||||||
|
is_public: true,
|
||||||
|
is_private: false,
|
||||||
|
is_standalone: false,
|
||||||
|
reminders: [],
|
||||||
|
recurrence: null,
|
||||||
|
category_id: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 67502,
|
||||||
|
creator: {
|
||||||
|
id: 1500588,
|
||||||
|
username: "foobar",
|
||||||
|
name: null,
|
||||||
|
avatar_template: "/user_avatar/localhost/foobar/{size}/1913_2.png",
|
||||||
|
assign_icon: "user-plus",
|
||||||
|
assign_path: "/u/foobar/activity/assigned",
|
||||||
|
},
|
||||||
|
sample_invitees: [],
|
||||||
|
watching_invitee: null,
|
||||||
|
starts_at: nextMonth,
|
||||||
|
ends_at: null,
|
||||||
|
timezone: "Asia/Calcutta",
|
||||||
|
stats: {
|
||||||
|
going: 0,
|
||||||
|
interested: 0,
|
||||||
|
not_going: 0,
|
||||||
|
invited: 0,
|
||||||
|
},
|
||||||
|
status: "public",
|
||||||
|
raw_invitees: ["trust_level_0"],
|
||||||
|
post: {
|
||||||
|
id: 67501,
|
||||||
|
post_number: 1,
|
||||||
|
url: "/t/this-is-an-event-2/18450/1",
|
||||||
|
topic: {
|
||||||
|
id: 18449,
|
||||||
|
title: "This is an event 2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: "Another Awesome Event",
|
||||||
|
can_act_on_discourse_post_event: true,
|
||||||
|
can_update_attendance: true,
|
||||||
|
is_expired: false,
|
||||||
|
is_ongoing: true,
|
||||||
|
should_display_invitees: false,
|
||||||
|
url: null,
|
||||||
|
custom_fields: {},
|
||||||
|
is_public: true,
|
||||||
|
is_private: false,
|
||||||
|
is_standalone: false,
|
||||||
|
reminders: [],
|
||||||
|
recurrence: null,
|
||||||
|
category_id: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue