-import gleam/dynamic
-import gleam/dynamic/decode
import gleam/int
import gleam/json
import gleam/list
import lustre/event
import plinth/browser/location
import plinth/browser/window
+import plinth/javascript/date
import rsvp
import shared
task_due_date: String,
subteam_filter: String,
task_search: String,
+ calendar_week: Int,
)
}
UnarchiveProject(Int)
ListView
DashboardView
+ CalendarView
+ PreviousWeek
+ NextWeek
}
fn init(_flags: Option(Nil)) -> #(Model, Effect(Message)) {
"/login" -> "login"
"/register" -> "register"
"/tasks" -> "tasks"
+ "/calendar" -> "calendar"
_ -> "dashboard"
}
task_due_date: "",
subteam_filter: "all",
task_search: "",
+ calendar_week: 0,
)
case page {
- "dashboard" | "tasks" | "login" -> #(
+ "dashboard" | "tasks" | "calendar" | "login" -> #(
model,
rsvp.get(
"/api/dashboard",
Nothing -> #(model, effect.none())
ListView -> #(Model(..model, page: "tasks"), effect.none())
DashboardView -> #(Model(..model, page: "dashboard"), effect.none())
+ CalendarView -> #(Model(..model, page: "calendar"), effect.none())
+ PreviousWeek -> #(
+ Model(..model, calendar_week: model.calendar_week - 1),
+ effect.none(),
+ )
+ NextWeek -> #(
+ Model(..model, calendar_week: model.calendar_week + 1),
+ effect.none(),
+ )
}
}
RegisterSubmitted,
)
"tasks" -> task_list_view(model)
+ "calendar" -> calendar_view(model)
_ -> dashboard_view(model)
}
}
]),
html.nav([], [
html.button([event.on_click(ListView)], [html.text("List view")]),
+ html.button([event.on_click(CalendarView)], [
+ html.text("Calendar"),
+ ]),
html.button([event.on_click(LogOut)], [html.text("Sign out")]),
]),
]),
html.button([event.on_click(DashboardView)], [
html.text("Board view"),
]),
+ html.button([event.on_click(CalendarView)], [html.text("Calendar")]),
html.button([event.on_click(LogOut)], [html.text("Sign out")]),
]),
]),
)
}
+fn calendar_view(model: Model) -> Element(Message) {
+ case model.dashboard {
+ None ->
+ html.main([attribute.class("auth")], [
+ styles(),
+ html.h1([], [html.text("Loading Iron Claw")]),
+ ])
+ Some(board) ->
+ html.main([attribute.class("page")], [
+ styles(),
+ html.header([], [
+ html.div([], [
+ html.text("FRC 972 · IRON CLAW"),
+ html.h1([], [html.text("calendar")]),
+ ]),
+ html.nav([], [
+ html.button([event.on_click(DashboardView)], [
+ html.text("Board view"),
+ ]),
+ html.button([event.on_click(ListView)], [html.text("List view")]),
+ html.button([event.on_click(LogOut)], [html.text("Sign out")]),
+ ]),
+ ]),
+ html.section([attribute.class("panel")], [
+ html.h2([], [html.text("Find tasks")]),
+ html.input([
+ attribute.class("task-search"),
+ attribute.placeholder("Search owner or assignees"),
+ event.on_input(TaskSearchChanged),
+ attribute.styles([
+ #("width", "100%"),
+ #("box-sizing", "border-box"),
+ ]),
+ ]),
+ html.select(
+ [
+ attribute.class("subteam-filter"),
+ event.on_change(SubteamFilterChanged),
+ attribute.styles([
+ #("width", "100%"),
+ #("margin-top", "8px"),
+ #("box-sizing", "border-box"),
+ #("padding", "12px 13px"),
+ #("border", "1px solid #d9cfc2"),
+ #("border-radius", "0"),
+ #("background", "#fffcf8"),
+ #("color", "#28231e"),
+ #("font", "inherit"),
+ #("appearance", "auto"),
+ ]),
+ ],
+ [
+ html.option([attribute.value("all")], "All subteams"),
+ html.option([attribute.value("none")], "No subteam"),
+ html.option([attribute.value("electrical")], "Electrical"),
+ html.option([attribute.value("software")], "Software"),
+ html.option([attribute.value("CAD")], "CAD"),
+ html.option([attribute.value("machining")], "Machining"),
+ html.option([attribute.value("operations")], "Operations"),
+ ],
+ ),
+ ]),
+ html.section(
+ [
+ attribute.class("panel"),
+ attribute.styles([#("padding", "0"), #("overflow", "hidden")]),
+ ],
+ [
+ html.div(
+ [
+ attribute.styles([
+ #("display", "flex"),
+ #("justify-content", "space-between"),
+ #("align-items", "center"),
+ #("padding", "14px 16px"),
+ #("border-bottom", "1px solid #e2d9cc"),
+ ]),
+ ],
+ [
+ html.button([event.on_click(PreviousWeek)], [
+ html.text("← Previous week"),
+ ]),
+ html.strong([], [html.text(week_label(model.calendar_week))]),
+ html.button([event.on_click(NextWeek)], [
+ html.text("Next week →"),
+ ]),
+ ],
+ ),
+ html.div(
+ [
+ attribute.class("calendar-grid"),
+ attribute.styles([
+ #("display", "grid"),
+ #("grid-template-columns", "repeat(7, minmax(150px, 1fr))"),
+ #("overflow-x", "auto"),
+ ]),
+ ],
+ list.map(current_week_dates(model.calendar_week), fn(x) {
+ let #(day_no_year, day, day_of_week) = x
+ calendar_day(
+ day_no_year,
+ day_of_week,
+ calendar_tasks(board.projects, model, day),
+ )
+ }),
+ ),
+ ],
+ ),
+ ])
+ }
+}
+
+fn current_week_dates(week_offset: Int) -> List(#(String, String, String)) {
+ let today = date.now()
+ let days_from_monday = case date.day(today) {
+ 0 -> 6
+ day -> day - 1
+ }
+ let current_week_start =
+ subtract_days(
+ date.year(today),
+ date.month(today) + 1,
+ date.date(today),
+ days_from_monday,
+ )
+ let start = shift_week(current_week_start, week_offset)
+
+ [0, 1, 2, 3, 4, 5, 6]
+ |> list.map(fn(offset) {
+ #(
+ format_date_no_year(add_days(start, offset)),
+ format_date(add_days(start, offset)),
+ offset |> num_to_day,
+ )
+ })
+}
+
+fn shift_week(date_value: #(Int, Int, Int), weeks: Int) -> #(Int, Int, Int) {
+ case weeks {
+ 0 -> date_value
+ value if value > 0 -> add_days(date_value, value * 7)
+ value -> subtract_date_days(date_value, 0 - value * 7)
+ }
+}
+
+fn subtract_date_days(
+ date_value: #(Int, Int, Int),
+ days: Int,
+) -> #(Int, Int, Int) {
+ let #(year, month, day) = date_value
+ subtract_days(year, month, day, days)
+}
+
+fn week_label(week_offset: Int) -> String {
+ let dates = current_week_dates(week_offset)
+ let first = list.first(dates) |> result.unwrap(#("", "", ""))
+ let last = list.last(dates) |> result.unwrap(#("", "", ""))
+ first.1 <> " – " <> last.1
+}
+
+fn num_to_day(day: Int) -> String {
+ case day {
+ 0 -> "Mon"
+ 1 -> "Tue"
+ 2 -> "Wed"
+ 3 -> "Thu"
+ 4 -> "Fri"
+ 5 -> "Sat"
+ 6 -> "Sun"
+ _ -> ""
+ }
+}
+
+fn subtract_days(
+ year: Int,
+ month: Int,
+ day: Int,
+ count: Int,
+) -> #(Int, Int, Int) {
+ case count {
+ 0 -> #(year, month, day)
+ _ -> {
+ let #(previous_year, previous_month, previous_day) = case day {
+ 1 -> {
+ let month = case month {
+ 1 -> 12
+ _ -> month - 1
+ }
+ let year = case month == 12 {
+ True -> year - 1
+ False -> year
+ }
+ #(year, month, days_in_month(year, month))
+ }
+ _ -> #(year, month, day - 1)
+ }
+ subtract_days(previous_year, previous_month, previous_day, count - 1)
+ }
+ }
+}
+
+fn add_days(date_value: #(Int, Int, Int), count: Int) -> #(Int, Int, Int) {
+ case count {
+ 0 -> date_value
+ _ -> {
+ let #(year, month, day) = date_value
+ let #(next_year, next_month, next_day) = case
+ day == days_in_month(year, month)
+ {
+ True ->
+ case month == 12 {
+ True -> #(year + 1, 1, 1)
+ False -> #(year, month + 1, 1)
+ }
+ False -> #(year, month, day + 1)
+ }
+ add_days(#(next_year, next_month, next_day), count - 1)
+ }
+ }
+}
+
+fn days_in_month(year: Int, month: Int) -> Int {
+ case month {
+ 2 -> {
+ let divisible_by_400 = int.remainder(year, by: 400) == Ok(0)
+ let divisible_by_4 = int.remainder(year, by: 4) == Ok(0)
+ let divisible_by_100 = int.remainder(year, by: 100) == Ok(0)
+ case divisible_by_400 || divisible_by_4 && !divisible_by_100 {
+ True -> 29
+ False -> 28
+ }
+ }
+ 4 | 6 | 9 | 11 -> 30
+ _ -> 31
+ }
+}
+
+fn format_date_no_year(date_value: #(Int, Int, Int)) -> String {
+ let #(_, month, day) = date_value
+ two_digits(month) <> "-" <> two_digits(day)
+}
+
+fn format_date(date_value: #(Int, Int, Int)) -> String {
+ let #(year, month, day) = date_value
+ int.to_string(year) <> "-" <> two_digits(month) <> "-" <> two_digits(day)
+}
+
+fn two_digits(value: Int) -> String {
+ let text = int.to_string(value)
+ case string.length(text) {
+ 1 -> "0" <> text
+ _ -> text
+ }
+}
+
+fn calendar_tasks(
+ projects: List(shared.Project),
+ model: Model,
+ date: String,
+) -> List(#(String, shared.Task)) {
+ task_rows(projects, model)
+ |> list.filter(fn(row) { row.1.due_date == date })
+}
+
+fn calendar_day(
+ date: String,
+ day_of_week: String,
+ tasks: List(#(String, shared.Task)),
+) -> Element(Message) {
+ html.div(
+ [
+ attribute.styles([
+ #("min-height", "260px"),
+ #("padding", "14px"),
+ #("border-right", "1px solid #e2d9cc"),
+ #("background", "#fff"),
+ ]),
+ ],
+ [
+ html.h3([attribute.style("margin-bottom", "1px")], [
+ html.text(day_of_week),
+ ]),
+ html.h3([attribute.style("margin-top", "0px")], [
+ html.text(date),
+ ]),
+ html.div(
+ [],
+ list.map(tasks, fn(row) {
+ let #(project, task) = row
+ html.div([attribute.class("task")], [
+ html.strong([], [html.text(task.title)]),
+ html.small([], [
+ html.strong([], [html.text("Project: ")]),
+ html.text(project),
+ ]),
+ html.span([], [
+ html.strong([], [
+ html.text("DEFCON " <> int.to_string(task.defcon)),
+ ]),
+ ]),
+ ])
+ }),
+ ),
+ ],
+ )
+}
+
fn project_sections(
model: Model,
projects: List(shared.Project),
return false;
return a.constructor === b.constructor;
}
+function remainderInt(a, b) {
+ if (b === 0) {
+ return 0;
+ } else {
+ return a % b;
+ }
+}
function makeError(variant, file, module, line, fn, message, extra) {
let error = new globalThis.Error(message);
error.gleam_error = variant;
}
}
}
+function first(list) {
+ if (list instanceof Empty) {
+ return new Error2(undefined);
+ } else {
+ let first$1 = list.head;
+ return new Ok(first$1);
+ }
+}
function filter_loop(loop$list, loop$fun, loop$acc) {
while (true) {
let list = loop$list;
}
function append_loop(loop$first, loop$second) {
while (true) {
- let first = loop$first;
+ let first2 = loop$first;
let second = loop$second;
- if (first instanceof Empty) {
+ if (first2 instanceof Empty) {
return second;
} else {
- let first$1 = first.head;
- let rest$1 = first.tail;
+ let first$1 = first2.head;
+ let rest$1 = first2.tail;
loop$first = rest$1;
loop$second = prepend(first$1, second);
}
}
}
-function append(first, second) {
- return append_loop(reverse(first), second);
+function append(first2, second) {
+ return append_loop(reverse(first2), second);
}
function prepend2(list, item) {
return prepend(item, list);
}
}
}
+function last(loop$list) {
+ while (true) {
+ let list = loop$list;
+ if (list instanceof Empty) {
+ return new Error2(undefined);
+ } else {
+ let $ = list.tail;
+ if ($ instanceof Empty) {
+ let last$1 = list.head;
+ return new Ok(last$1);
+ } else {
+ let rest$1 = $;
+ loop$list = rest$1;
+ }
+ }
+ }
+}
// build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs
class DecodeError extends CustomType {
}
}
}
-function one_of(first, alternatives) {
+function one_of(first2, alternatives) {
return new Decoder((dynamic_data) => {
- let $ = first.function(dynamic_data);
+ let $ = first2.function(dynamic_data);
let layer = $;
let errors = $[1];
if (errors instanceof Empty) {
function to_string(term) {
return term.toString();
}
+function string_length(string4) {
+ if (string4 === "") {
+ return 0;
+ }
+ const iterator = graphemes_iterator(string4);
+ if (iterator) {
+ let i = 0;
+ for (const _ of iterator) {
+ i++;
+ }
+ return i;
+ } else {
+ return string4.match(/./gsu).length;
+ }
+}
function graphemes(string4) {
const iterator = graphemes_iterator(string4);
if (iterator) {
}
#dict(map4) {
let body = "dict.from_list([";
- let first = true;
+ let first2 = true;
body = fold(map4, body, (body2, key, value) => {
- if (!first)
+ if (!first2)
body2 = body2 + ", ";
- first = false;
+ first2 = false;
return body2 + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")";
});
return body + "])";
}
}
}
+function remainder(dividend, divisor) {
+ if (divisor === 0) {
+ return new Error2(undefined);
+ } else {
+ let divisor$1 = divisor;
+ return new Ok(remainderInt(dividend, divisor$1));
+ }
+}
// build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs
class All extends CustomType {
const throttle = throttles.get(type);
if (throttle) {
const now = Date.now();
- const last = throttle.last || 0;
- if (now > last + throttle.delay) {
+ const last2 = throttle.last || 0;
+ if (now > last2 + throttle.delay) {
throttle.last = now;
throttle.lastEvent = event3;
this.#dispatch(event3, data);
}
// build/dev/javascript/gleam_stdlib/gleam/pair.mjs
-function new$5(first, second) {
- return [first, second];
+function new$5(first2, second) {
+ return [first2, second];
}
// build/dev/javascript/lustre/lustre/event.mjs
}
}
+// build/dev/javascript/plinth/date_ffi.mjs
+function now() {
+ return new Date;
+}
+function year(d) {
+ return d.getFullYear();
+}
+function month(d) {
+ return d.getMonth();
+}
+function day(d) {
+ return d.getDay();
+}
+function date(d) {
+ return d.getDate();
+}
+
// build/dev/javascript/gleam_stdlib/gleam/uri.mjs
class Uri extends CustomType {
constructor(scheme, userinfo, host, port, path, query, fragment3) {
var FILEPATH = "src/client.gleam";
class Model extends CustomType {
- constructor(page, dashboard, error, username, password, invite_password, project_name, project_description, task_title, task_description, task_assignee, task_assignees, task_subteams, task_due_date, subteam_filter, task_search) {
+ constructor(page, dashboard, error, username, password, invite_password, project_name, project_description, task_title, task_description, task_assignee, task_assignees, task_subteams, task_due_date, subteam_filter, task_search, calendar_week) {
super();
this.page = page;
this.dashboard = dashboard;
this.task_due_date = task_due_date;
this.subteam_filter = subteam_filter;
this.task_search = task_search;
+ this.calendar_week = calendar_week;
}
}
class DashboardView extends CustomType {
}
var Message$DashboardView$const = new DashboardView;
+class CalendarView extends CustomType {
+}
+var Message$CalendarView$const = new CalendarView;
+class PreviousWeek extends CustomType {
+}
+var Message$PreviousWeek$const = new PreviousWeek;
+class NextWeek extends CustomType {
+}
+var Message$NextWeek$const = new NextWeek;
function subteam_checkbox(selected, value3, label2) {
return label(toList([
class$("subteam-option"),
])),
nav(List$Empty$const, toList([
button(toList([on_click(Message$ListView$const)]), toList([text3("List view")])),
+ button(toList([on_click(Message$CalendarView$const)]), toList([text3("Calendar")])),
button(toList([on_click(Message$LogOut$const)]), toList([text3("Sign out")]))
]))
])),
]));
}
}
+function task_rows(projects, model) {
+ let _pipe = projects;
+ return flat_map(_pipe, (project) => {
+ let _pipe$1 = project.tasks;
+ let _pipe$2 = filter(_pipe$1, (task) => {
+ return task_matches(model, task);
+ });
+ return map2(_pipe$2, (task) => {
+ return [project.name, task];
+ });
+ });
+}
+function calendar_tasks(projects, model, date2) {
+ let _pipe = task_rows(projects, model);
+ return filter(_pipe, (row) => {
+ return row[1].due_date === date2;
+ });
+}
+function calendar_day(date2, day_of_week, tasks) {
+ return div(toList([
+ styles(toList([
+ ["min-height", "260px"],
+ ["padding", "14px"],
+ ["border-right", "1px solid #e2d9cc"],
+ ["background", "#fff"]
+ ]))
+ ]), toList([
+ h3(toList([style("margin-bottom", "1px")]), toList([text3(day_of_week)])),
+ h3(toList([style("margin-top", "0px")]), toList([text3(date2)])),
+ div(List$Empty$const, map2(tasks, (row) => {
+ let project = row[0];
+ let task = row[1];
+ return div(toList([class$("task")]), toList([
+ strong(List$Empty$const, toList([text3(task.title)])),
+ small(List$Empty$const, toList([
+ strong(List$Empty$const, toList([text3("Project: ")])),
+ text3(project)
+ ])),
+ span(List$Empty$const, toList([
+ strong(List$Empty$const, toList([
+ text3("DEFCON " + to_string(task.defcon))
+ ]))
+ ]))
+ ]));
+ }))
+ ]));
+}
+function num_to_day(day2) {
+ if (day2 === 0) {
+ return "Mon";
+ } else if (day2 === 1) {
+ return "Tue";
+ } else if (day2 === 2) {
+ return "Wed";
+ } else if (day2 === 3) {
+ return "Thu";
+ } else if (day2 === 4) {
+ return "Fri";
+ } else if (day2 === 5) {
+ return "Sat";
+ } else if (day2 === 6) {
+ return "Sun";
+ } else {
+ return "";
+ }
+}
+function days_in_month(year2, month2) {
+ if (month2 === 2) {
+ let divisible_by_400 = isEqual(remainder(year2, 400), new Ok(0));
+ let divisible_by_4 = isEqual(remainder(year2, 4), new Ok(0));
+ let divisible_by_100 = isEqual(remainder(year2, 100), new Ok(0));
+ let $ = divisible_by_400 || divisible_by_4 && !divisible_by_100;
+ if ($) {
+ return 29;
+ } else {
+ return 28;
+ }
+ } else if (month2 === 4) {
+ return 30;
+ } else if (month2 === 6) {
+ return 30;
+ } else if (month2 === 9) {
+ return 30;
+ } else if (month2 === 11) {
+ return 30;
+ } else {
+ return 31;
+ }
+}
+function add_days(loop$date_value, loop$count) {
+ while (true) {
+ let date_value = loop$date_value;
+ let count = loop$count;
+ if (count === 0) {
+ return date_value;
+ } else {
+ let year2 = date_value[0];
+ let month2 = date_value[1];
+ let day2 = date_value[2];
+ let _block;
+ let $1 = day2 === days_in_month(year2, month2);
+ if ($1) {
+ let $2 = month2 === 12;
+ if ($2) {
+ _block = [year2 + 1, 1, 1];
+ } else {
+ _block = [year2, month2 + 1, 1];
+ }
+ } else {
+ _block = [year2, month2, day2 + 1];
+ }
+ let $ = _block;
+ let next_year = $[0];
+ let next_month = $[1];
+ let next_day = $[2];
+ loop$date_value = [next_year, next_month, next_day];
+ loop$count = count - 1;
+ }
+ }
+}
+function two_digits(value3) {
+ let text4 = to_string(value3);
+ let $ = string_length(text4);
+ if ($ === 1) {
+ return "0" + text4;
+ } else {
+ return text4;
+ }
+}
+function format_date(date_value) {
+ let year2 = date_value[0];
+ let month2 = date_value[1];
+ let day2 = date_value[2];
+ return to_string(year2) + "-" + two_digits(month2) + "-" + two_digits(day2);
+}
+function format_date_no_year(date_value) {
+ let month2 = date_value[1];
+ let day2 = date_value[2];
+ return two_digits(month2) + "-" + two_digits(day2);
+}
+function subtract_days(loop$year, loop$month, loop$day, loop$count) {
+ while (true) {
+ let year2 = loop$year;
+ let month2 = loop$month;
+ let day2 = loop$day;
+ let count = loop$count;
+ if (count === 0) {
+ return [year2, month2, day2];
+ } else {
+ let _block;
+ if (day2 === 1) {
+ let _block$1;
+ if (month2 === 1) {
+ _block$1 = 12;
+ } else {
+ _block$1 = month2 - 1;
+ }
+ let month$1 = _block$1;
+ let _block$2;
+ let $1 = month$1 === 12;
+ if ($1) {
+ _block$2 = year2 - 1;
+ } else {
+ _block$2 = year2;
+ }
+ let year$1 = _block$2;
+ _block = [year$1, month$1, days_in_month(year$1, month$1)];
+ } else {
+ _block = [year2, month2, day2 - 1];
+ }
+ let $ = _block;
+ let previous_year = $[0];
+ let previous_month = $[1];
+ let previous_day = $[2];
+ loop$year = previous_year;
+ loop$month = previous_month;
+ loop$day = previous_day;
+ loop$count = count - 1;
+ }
+ }
+}
+function subtract_date_days(date_value, days) {
+ let year2 = date_value[0];
+ let month2 = date_value[1];
+ let day2 = date_value[2];
+ return subtract_days(year2, month2, day2, days);
+}
+function shift_week(date_value, weeks) {
+ if (weeks === 0) {
+ return date_value;
+ } else {
+ let value3 = weeks;
+ if (value3 > 0) {
+ return add_days(date_value, value3 * 7);
+ } else {
+ let value4 = weeks;
+ return subtract_date_days(date_value, 0 - value4 * 7);
+ }
+ }
+}
+function current_week_dates(week_offset) {
+ let today = now();
+ let _block;
+ let $ = day(today);
+ if ($ === 0) {
+ _block = 6;
+ } else {
+ let day2 = $;
+ _block = day2 - 1;
+ }
+ let days_from_monday = _block;
+ let current_week_start = subtract_days(year(today), month(today) + 1, date(today), days_from_monday);
+ let start5 = shift_week(current_week_start, week_offset);
+ let _pipe = toList([0, 1, 2, 3, 4, 5, 6]);
+ return map2(_pipe, (offset) => {
+ return [
+ format_date_no_year(add_days(start5, offset)),
+ format_date(add_days(start5, offset)),
+ (() => {
+ let _pipe$1 = offset;
+ return num_to_day(_pipe$1);
+ })()
+ ];
+ });
+}
+function week_label(week_offset) {
+ let dates = current_week_dates(week_offset);
+ let _block;
+ let _pipe = first(dates);
+ _block = unwrap2(_pipe, ["", "", ""]);
+ let first2 = _block;
+ let _block$1;
+ let _pipe$1 = last(dates);
+ _block$1 = unwrap2(_pipe$1, ["", "", ""]);
+ let last2 = _block$1;
+ return first2[1] + " – " + last2[1];
+}
+function calendar_view(model) {
+ let $ = model.dashboard;
+ if ($ instanceof Some) {
+ let board = $[0];
+ return main(toList([class$("page")]), toList([
+ styles2(),
+ header(List$Empty$const, toList([
+ div(List$Empty$const, toList([
+ text3("FRC 972 · IRON CLAW"),
+ h1(List$Empty$const, toList([text3("calendar")]))
+ ])),
+ nav(List$Empty$const, toList([
+ button(toList([on_click(Message$DashboardView$const)]), toList([text3("Board view")])),
+ button(toList([on_click(Message$ListView$const)]), toList([text3("List view")])),
+ button(toList([on_click(Message$LogOut$const)]), toList([text3("Sign out")]))
+ ]))
+ ])),
+ section(toList([class$("panel")]), toList([
+ h2(List$Empty$const, toList([text3("Find tasks")])),
+ input(toList([
+ class$("task-search"),
+ placeholder("Search owner or assignees"),
+ on_input((var0) => {
+ return new TaskSearchChanged(var0);
+ }),
+ styles(toList([["width", "100%"], ["box-sizing", "border-box"]]))
+ ])),
+ select(toList([
+ class$("subteam-filter"),
+ on_change((var0) => {
+ return new SubteamFilterChanged(var0);
+ }),
+ styles(toList([
+ ["width", "100%"],
+ ["margin-top", "8px"],
+ ["box-sizing", "border-box"],
+ ["padding", "12px 13px"],
+ ["border", "1px solid #d9cfc2"],
+ ["border-radius", "0"],
+ ["background", "#fffcf8"],
+ ["color", "#28231e"],
+ ["font", "inherit"],
+ ["appearance", "auto"]
+ ]))
+ ]), toList([
+ option(toList([value("all")]), "All subteams"),
+ option(toList([value("none")]), "No subteam"),
+ option(toList([value("electrical")]), "Electrical"),
+ option(toList([value("software")]), "Software"),
+ option(toList([value("CAD")]), "CAD"),
+ option(toList([value("machining")]), "Machining"),
+ option(toList([value("operations")]), "Operations")
+ ]))
+ ])),
+ section(toList([
+ class$("panel"),
+ styles(toList([["padding", "0"], ["overflow", "hidden"]]))
+ ]), toList([
+ div(toList([
+ styles(toList([
+ ["display", "flex"],
+ ["justify-content", "space-between"],
+ ["align-items", "center"],
+ ["padding", "14px 16px"],
+ ["border-bottom", "1px solid #e2d9cc"]
+ ]))
+ ]), toList([
+ button(toList([on_click(Message$PreviousWeek$const)]), toList([text3("← Previous week")])),
+ strong(List$Empty$const, toList([text3(week_label(model.calendar_week))])),
+ button(toList([on_click(Message$NextWeek$const)]), toList([text3("Next week →")]))
+ ])),
+ div(toList([
+ class$("calendar-grid"),
+ styles(toList([
+ ["display", "grid"],
+ ["grid-template-columns", "repeat(7, minmax(150px, 1fr))"],
+ ["overflow-x", "auto"]
+ ]))
+ ]), map2(current_week_dates(model.calendar_week), (x2) => {
+ let day_no_year = x2[0];
+ let day2 = x2[1];
+ let day_of_week = x2[2];
+ return calendar_day(day_no_year, day_of_week, calendar_tasks(board.projects, model, day2));
+ }))
+ ]))
+ ]));
+ } else {
+ return main(toList([class$("auth")]), toList([
+ styles2(),
+ h1(List$Empty$const, toList([text3("Loading Iron Claw")]))
+ ]));
+ }
+}
function table_cell(children) {
return td(toList([
styles(toList([
]))
]));
}
-function task_rows(projects, model) {
- let _pipe = projects;
- return flat_map(_pipe, (project) => {
- let _pipe$1 = project.tasks;
- let _pipe$2 = filter(_pipe$1, (task) => {
- return task_matches(model, task);
- });
- return map2(_pipe$2, (task) => {
- return [project.name, task];
- });
- });
-}
function table_heading(label2) {
return th(toList([
styles(toList([
])),
nav(List$Empty$const, toList([
button(toList([on_click(Message$DashboardView$const)]), toList([text3("Board view")])),
+ button(toList([on_click(Message$CalendarView$const)]), toList([text3("Calendar")])),
button(toList([on_click(Message$LogOut$const)]), toList([text3("Sign out")]))
]))
])),
});
} else if ($ === "tasks") {
return task_list_view(model);
+ } else if ($ === "calendar") {
+ return calendar_view(model);
} else {
return dashboard_view(model);
}
let projects = filter(board.projects, (project) => {
return project.id !== project_id;
});
- return new Model(model.page, new Some(new Dashboard(board.username, projects)), model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search);
+ return new Model(model.page, new Some(new Dashboard(board.username, projects)), model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week);
} else {
return model;
}
});
return new Project(project.id, project.name, project.description, project.archived, tasks);
});
- return new Model(model.page, new Some(new Dashboard(board.username, projects)), model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search);
+ return new Model(model.page, new Some(new Dashboard(board.username, projects)), model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week);
} else {
return model;
}
});
return new Project(project.id, project.name, project.description, project.archived, tasks);
});
- return new Model(model.page, new Some(new Dashboard(board.username, projects)), model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search);
+ return new Model(model.page, new Some(new Dashboard(board.username, projects)), model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week);
} else {
return model;
}
if ($ instanceof Ok) {
let value3 = $[0];
return [
- new Model(model.page, new Some(value3), Option$None$const, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, new Some(value3), Option$None$const, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else {
return [
- new Model("login", model.dashboard, new Some("Please sign in to continue."), model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model("login", model.dashboard, new Some("Please sign in to continue."), model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
}
_block = new_project_json(_pipe);
let body2 = _block;
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, "", "", model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, "", "", model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
post("/api/projects", body2, expect_any_response((result) => {
return new Saved((() => {
let _pipe$1 = result;
_block = new_task_json(_pipe);
let body2 = _block;
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, "", "", "", "", List$Empty$const, "", model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, "", "", "", "", List$Empty$const, "", model.subteam_filter, model.task_search, model.calendar_week),
post("/api/projects/" + to_string(project_id) + "/tasks", body2, expect_any_response((result) => {
return new Saved((() => {
let _pipe$1 = result;
let $ = message2[0];
if ($ instanceof Ok) {
return [
- new Model("dashboard", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model("dashboard", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
get3("/api/dashboard", expect_json(dashboard_decoder(), (var0) => {
return new DashboardLoaded(var0);
}))
];
} else {
return [
- new Model(model.page, model.dashboard, new Some("Invalid username or password."), model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, new Some("Invalid username or password."), model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
}
let $ = message2[0];
if ($ instanceof Ok) {
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, "", "", "", "", "", "", "", List$Empty$const, "", model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, "", "", "", "", "", "", "", List$Empty$const, "", model.subteam_filter, model.task_search, model.calendar_week),
get3("/api/dashboard", expect_json(dashboard_decoder(), (var0) => {
return new DashboardLoaded(var0);
}))
];
} else {
return [
- new Model(model.page, model.dashboard, new Some("The request could not be saved."), model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, new Some("The request could not be saved."), model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
}
} else if (message2 instanceof UsernameChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, value3, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, value3, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof PasswordChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, value3, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, value3, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof ProjectNameChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, value3, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, value3, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof ProjectDescriptionChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, value3, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, value3, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskTitleChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, value3, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, value3, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskDescriptionChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, value3, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, value3, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskAssigneeChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, value3, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, value3, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskAssigneesChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, value3, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, value3, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskDueDateChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, value3, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, value3, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskSubteamChanged) {
let subteam = message2[0];
let checked2 = message2[1];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, toggle_subteam(model.task_subteams, subteam, checked2), model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, toggle_subteam(model.task_subteams, subteam, checked2), model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof SubteamFilterChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, value3, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, value3, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof TaskSearchChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, value3),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, value3, model.calendar_week),
none()
];
} else if (message2 instanceof InvitePasswordChanged) {
let value3 = message2[0];
return [
- new Model(model.page, model.dashboard, model.error, model.username, model.password, value3, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, value3, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
none()
];
} else if (message2 instanceof DeleteTask) {
];
} else if (message2 instanceof LogOut) {
return [
- new Model("login", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model("login", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
get3("/api/logout", expect_ok_response((_) => {
return Message$Nothing$const;
}))
];
} else if (message2 instanceof ListView) {
return [
- new Model("tasks", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model("tasks", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
+ none()
+ ];
+ } else if (message2 instanceof DashboardView) {
+ return [
+ new Model("dashboard", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
+ none()
+ ];
+ } else if (message2 instanceof CalendarView) {
+ return [
+ new Model("calendar", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week),
+ none()
+ ];
+ } else if (message2 instanceof PreviousWeek) {
+ return [
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week - 1),
none()
];
} else {
return [
- new Model("dashboard", model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search),
+ new Model(model.page, model.dashboard, model.error, model.username, model.password, model.invite_password, model.project_name, model.project_description, model.task_title, model.task_description, model.task_assignee, model.task_assignees, model.task_subteams, model.task_due_date, model.subteam_filter, model.task_search, model.calendar_week + 1),
none()
];
}
_block = "register";
} else if ($ === "/tasks") {
_block = "tasks";
+ } else if ($ === "/calendar") {
+ _block = "calendar";
} else {
_block = "dashboard";
}
let page = _block;
- let model = new Model(page, Option$None$const, Option$None$const, "", "", "", "", "", "", "", "", "", List$Empty$const, "", "all", "");
+ let model = new Model(page, Option$None$const, Option$None$const, "", "", "", "", "", "", "", "", "", List$Empty$const, "", "all", "", 0);
if (page === "dashboard") {
return [
model,
return new DashboardLoaded(var0);
}))
];
+ } else if (page === "calendar") {
+ return [
+ model,
+ get3("/api/dashboard", expect_json(dashboard_decoder(), (var0) => {
+ return new DashboardLoaded(var0);
+ }))
+ ];
} else if (page === "login") {
return [
model,
let app = application(init, update2, view);
let $ = start4(app, "#app", Option$None$const);
if (!($ instanceof Ok)) {
- throw makeError("let_assert", FILEPATH, "client", 23, "main", "Pattern match failed, no pattern matched the value.", { value: $, start: 527, end: 577, pattern_start: 538, pattern_end: 543 });
+ throw makeError("let_assert", FILEPATH, "client", 22, "main", "Pattern match failed, no pattern matched the value.", { value: $, start: 508, end: 558, pattern_start: 519, pattern_end: 524 });
}
return;
}