Nothing
ArchiveProject(Int)
UnarchiveProject(Int)
+ ListView
+ DashboardView
}
fn init(_flags: Option(Nil)) -> #(Model, Effect(Message)) {
let page = case current_path() {
"/login" -> "login"
"/register" -> "register"
+ "/tasks" -> "tasks"
_ -> "dashboard"
}
)
case page {
- "dashboard" | "login" -> #(
+ "dashboard" | "tasks" | "login" -> #(
model,
rsvp.get(
"/api/dashboard",
fn update(model: Model, message: Message) -> #(Model, Effect(Message)) {
case message {
DashboardLoaded(Ok(value)) -> {
- #(
- Model(..model, page: "dashboard", dashboard: Some(value), error: None),
- effect.none(),
- )
+ #(Model(..model, dashboard: Some(value), error: None), effect.none())
}
DashboardLoaded(Error(_)) -> {
#(
field(fields, "due_date"),
list.key_find(fields, "defcon")
|> result.unwrap("")
- |> dynamic.string
- |> decode.run(decode.int)
+ |> int.parse
|> result.unwrap(0),
)
|> shared.new_task_json
rsvp.get("/api/logout", rsvp.expect_ok_response(fn(_) { Nothing })),
)
Nothing -> #(model, effect.none())
+ ListView -> #(Model(..model, page: "tasks"), effect.none())
+ DashboardView -> #(Model(..model, page: "dashboard"), effect.none())
}
}
"Create account",
RegisterSubmitted,
)
+ "tasks" -> task_list_view(model)
_ -> dashboard_view(model)
}
}
html.text("FRC 972 · IRON CLAW"),
html.h1([], [html.text("project board")]),
]),
- html.button([event.on_click(LogOut)], [
- html.text("Sign out"),
+ html.nav([], [
+ html.button([event.on_click(ListView)], [html.text("List view")]),
+ html.button([event.on_click(LogOut)], [html.text("Sign out")]),
]),
]),
html.section([attribute.class("hero")], [
}
}
+fn task_list_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("task list")]),
+ ]),
+ html.nav([], [
+ html.button([event.on_click(DashboardView)], [
+ html.text("Board 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.styles([
+ #("width", "100%"),
+ #("box-sizing", "border-box"),
+ ]),
+ attribute.placeholder("Search owner or assignees"),
+ event.on_input(TaskSearchChanged),
+ ]),
+ html.select(
+ [
+ attribute.class("subteam-filter"),
+ 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"),
+ ]),
+ event.on_change(SubteamFilterChanged),
+ ],
+ [
+ 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 table-panel"),
+ attribute.styles([#("padding", "0")]),
+ ],
+ [
+ html.div(
+ [
+ attribute.class("task-table-scroll"),
+ attribute.styles([#("overflow-x", "auto")]),
+ ],
+ [
+ html.table(
+ [
+ attribute.styles([
+ #("width", "100%"),
+ #("border-collapse", "collapse"),
+ #("min-width", "760px"),
+ ]),
+ ],
+ [
+ html.thead([], [
+ html.tr([], [
+ table_heading("Task"),
+ table_heading("Project"),
+ table_heading("Owner"),
+ table_heading("Assignees"),
+ table_heading("Subteams"),
+ table_heading("Due"),
+ table_heading("DEFCON"),
+ table_heading("Status"),
+ ]),
+ ]),
+ html.tbody(
+ [],
+ list.map(
+ task_rows(board.projects, model)
+ |> list.sort(fn(a, b) {
+ case int.compare(b.1.defcon, a.1.defcon) {
+ order.Eq ->
+ string.compare(a.1.due_date, b.1.due_date)
+ x -> x
+ }
+ }),
+ task_row,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ ])
+ }
+}
+
+fn table_heading(label: String) -> Element(Message) {
+ html.th(
+ [
+ attribute.styles([
+ #("padding", "13px 15px"),
+ #("background", "#f0ebe3"),
+ #("border-bottom", "1px solid #d9cfc2"),
+ #("color", "#796f64"),
+ #("font-size", ".72rem"),
+ #("letter-spacing", ".06em"),
+ #("text-align", "left"),
+ #("text-transform", "uppercase"),
+ #("white-space", "nowrap"),
+ ]),
+ ],
+ [html.text(label)],
+ )
+}
+
+fn task_rows(
+ projects: List(shared.Project),
+ model: Model,
+) -> List(#(String, shared.Task)) {
+ projects
+ |> list.flat_map(fn(project) {
+ project.tasks
+ |> list.filter(fn(task) { task_matches(model, task) })
+ |> list.map(fn(task) { #(project.name, task) })
+ })
+}
+
+fn task_row(row: #(String, shared.Task)) -> Element(Message) {
+ let #(project_name, task) = row
+ html.tr([], [
+ table_cell([html.strong([], [html.text(task.title)])]),
+ table_cell([html.text(project_name)]),
+ table_cell([html.text(if_blank(task.assignee, "Unassigned"))]),
+ table_cell([html.text(if_blank(task.assignees, "None"))]),
+ table_cell([html.text(if_blank(join_subteams(task.subteams), "None"))]),
+ table_cell([
+ html.text(case task.due_date {
+ "" -> "No due date"
+ date -> date
+ }),
+ ]),
+ table_cell([
+ html.span([attribute.class("task-status")], [
+ html.text(int.to_string(task.defcon)),
+ ]),
+ ]),
+ table_cell([
+ html.span([attribute.class("task-status")], [
+ html.text(task.status),
+ ]),
+ ]),
+ ])
+}
+
+fn table_cell(children: List(Element(Message))) -> Element(Message) {
+ html.td(
+ [
+ attribute.styles([
+ #("padding", "13px 15px"),
+ #("border-bottom", "1px solid #eee6dc"),
+ #("color", "#4e463e"),
+ #("font-size", ".82rem"),
+ #("text-align", "left"),
+ #("vertical-align", "top"),
+ ]),
+ ],
+ children,
+ )
+}
+
fn project_sections(
model: Model,
projects: List(shared.Project),
],
tasks
|> list.sort(fn(a, b) {
- case int.compare(a.defcon, b.defcon) {
+ case int.compare(b.defcon, a.defcon) {
order.Eq -> string.compare(a.due_date, b.due_date)
a -> a
}
function prepend2(list, item) {
return prepend(item, list);
}
+function flatten_loop(loop$lists, loop$acc) {
+ while (true) {
+ let lists = loop$lists;
+ let acc = loop$acc;
+ if (lists instanceof Empty) {
+ return reverse(acc);
+ } else {
+ let list = lists.head;
+ let further_lists = lists.tail;
+ loop$lists = further_lists;
+ loop$acc = reverse_and_prepend(list, acc);
+ }
+ }
+}
+function flatten(lists) {
+ return flatten_loop(lists, List$Empty$const);
+}
+function flat_map(list, fun) {
+ return flatten(map2(list, fun));
+}
function fold2(loop$list, loop$initial, loop$fun) {
while (true) {
let list = loop$list;
}
// build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs
+var Nil = undefined;
function identity(x) {
return x;
}
+function parse_int(value) {
+ if (/^[-+]?(\d+)$/.test(value)) {
+ return Result$Ok(parseInt(value));
+ } else {
+ return Result$Error(Nil);
+ }
+}
function to_string(term) {
return term.toString();
}
return Result$isOk(data) || Result$isError(data);
}
+// build/dev/javascript/gleam_stdlib/gleam/int.mjs
+function compare2(a, b) {
+ let $ = a === b;
+ if ($) {
+ return Order$Eq$const;
+ } else {
+ let $1 = a < b;
+ if ($1) {
+ return Order$Lt$const;
+ } else {
+ return Order$Gt$const;
+ }
+ }
+}
+
// build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs
class All extends CustomType {
}
class Trailing extends CustomType {
}
var Direction$Trailing$const = new Trailing;
-function compare2(a, b) {
+function compare3(a, b) {
let $ = a === b;
if ($) {
return Order$Eq$const;
var GT = /* @__PURE__ */ Order$Gt();
var LT = /* @__PURE__ */ Order$Lt();
var EQ = /* @__PURE__ */ Order$Eq();
-function compare3(a, b) {
+function compare4(a, b) {
if (a.name === b.name) {
return EQ;
} else if (a.name < b.name) {
} else {
let _pipe = attributes;
let _pipe$1 = sort(_pipe, (a, b) => {
- return compare3(b, a);
+ return compare4(b, a);
});
return merge(_pipe$1, empty_list);
}
function main(attrs, children) {
return element2("main", attrs, children);
}
+function nav(attrs, children) {
+ return element2("nav", attrs, children);
+}
function section(attrs, children) {
return element2("section", attrs, children);
}
function strong(attrs, children) {
return element2("strong", attrs, children);
}
+function table(attrs, children) {
+ return element2("table", attrs, children);
+}
+function tbody(attrs, children) {
+ return element2("tbody", attrs, children);
+}
+function td(attrs, children) {
+ return element2("td", attrs, children);
+}
+function th(attrs, children) {
+ return element2("th", attrs, children);
+}
+function thead(attrs, children) {
+ return element2("thead", attrs, children);
+}
+function tr(attrs, children) {
+ return element2("tr", attrs, children);
+}
function button(attrs, children) {
return element2("button", attrs, children);
}
let remaining_old = old.tail;
let next = new$5.head;
let remaining_new = new$5.tail;
- let $ = compare3(prev, next);
+ let $ = compare4(prev, next);
if ($ instanceof Lt) {
if (prev instanceof Event2) {
let name2 = prev.name;
this[0] = $0;
}
}
+class ListView extends CustomType {
+}
+var Message$ListView$const = new ListView;
+class DashboardView extends CustomType {
+}
+var Message$DashboardView$const = new DashboardView;
function subteam_checkbox(selected, value3, label2) {
return label(toList([
class$("subteam-option"),
]), (() => {
let _pipe = tasks;
let _pipe$1 = sort(_pipe, (a2, b) => {
- return compare2(a2.due_date, b.due_date);
+ let $ = compare2(b.defcon, a2.defcon);
+ if ($ instanceof Eq) {
+ return compare3(a2.due_date, b.due_date);
+ } else {
+ return $;
+ }
});
return map2(_pipe$1, task_view);
})()));
text3("FRC 972 · IRON CLAW"),
h1(List$Empty$const, toList([text3("project board")]))
])),
- button(toList([on_click(Message$LogOut$const)]), toList([text3("Sign out")]))
+ nav(List$Empty$const, toList([
+ 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$("hero")]), toList([text3("wassup")])),
section(toList([class$("panel")]), toList([
]));
}
}
+function table_cell(children) {
+ return td(toList([
+ styles(toList([
+ ["padding", "13px 15px"],
+ ["border-bottom", "1px solid #eee6dc"],
+ ["color", "#4e463e"],
+ ["font-size", ".82rem"],
+ ["text-align", "left"],
+ ["vertical-align", "top"]
+ ]))
+ ]), children);
+}
+function task_row(row) {
+ let project_name = row[0];
+ let task = row[1];
+ return tr(List$Empty$const, toList([
+ table_cell(toList([
+ strong(List$Empty$const, toList([text3(task.title)]))
+ ])),
+ table_cell(toList([text3(project_name)])),
+ table_cell(toList([text3(if_blank(task.assignee, "Unassigned"))])),
+ table_cell(toList([text3(if_blank(task.assignees, "None"))])),
+ table_cell(toList([text3(if_blank(join_subteams(task.subteams), "None"))])),
+ table_cell(toList([
+ text3((() => {
+ let $ = task.due_date;
+ if ($ === "") {
+ return "No due date";
+ } else {
+ return $;
+ }
+ })())
+ ])),
+ table_cell(toList([
+ span(toList([class$("task-status")]), toList([text3(to_string(task.defcon))]))
+ ])),
+ table_cell(toList([
+ span(toList([class$("task-status")]), toList([text3(task.status)]))
+ ]))
+ ]));
+}
+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([
+ ["padding", "13px 15px"],
+ ["background", "#f0ebe3"],
+ ["border-bottom", "1px solid #d9cfc2"],
+ ["color", "#796f64"],
+ ["font-size", ".72rem"],
+ ["letter-spacing", ".06em"],
+ ["text-align", "left"],
+ ["text-transform", "uppercase"],
+ ["white-space", "nowrap"]
+ ]))
+ ]), toList([text3(label2)]));
+}
+function task_list_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("task list")]))
+ ])),
+ nav(List$Empty$const, toList([
+ button(toList([on_click(Message$DashboardView$const)]), toList([text3("Board 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"),
+ styles(toList([["width", "100%"], ["box-sizing", "border-box"]])),
+ placeholder("Search owner or assignees"),
+ on_input((var0) => {
+ return new TaskSearchChanged(var0);
+ })
+ ])),
+ select(toList([
+ class$("subteam-filter"),
+ 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"]
+ ])),
+ on_change((var0) => {
+ return new SubteamFilterChanged(var0);
+ })
+ ]), 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 table-panel"),
+ styles(toList([["padding", "0"]]))
+ ]), toList([
+ div(toList([
+ class$("task-table-scroll"),
+ styles(toList([["overflow-x", "auto"]]))
+ ]), toList([
+ table(toList([
+ styles(toList([
+ ["width", "100%"],
+ ["border-collapse", "collapse"],
+ ["min-width", "760px"]
+ ]))
+ ]), toList([
+ thead(List$Empty$const, toList([
+ tr(List$Empty$const, toList([
+ table_heading("Task"),
+ table_heading("Project"),
+ table_heading("Owner"),
+ table_heading("Assignees"),
+ table_heading("Subteams"),
+ table_heading("Due"),
+ table_heading("DEFCON"),
+ table_heading("Status")
+ ]))
+ ])),
+ tbody(List$Empty$const, map2((() => {
+ let _pipe = task_rows(board.projects, model);
+ return sort(_pipe, (a2, b) => {
+ let $1 = compare2(b[1].defcon, a2[1].defcon);
+ if ($1 instanceof Eq) {
+ return compare3(a2[1].due_date, b[1].due_date);
+ } else {
+ return $1;
+ }
+ });
+ })(), task_row))
+ ]))
+ ]))
+ ]))
+ ]));
+ } else {
+ return main(toList([class$("auth")]), toList([
+ styles2(),
+ h1(List$Empty$const, toList([text3("Loading Iron Claw")]))
+ ]));
+ }
+}
function creat_account_view(model, title2, submit, on_submit2) {
let _block;
let $ = submit === "Log in";
return creat_account_view(model, "Create account", "Create account", (var0) => {
return new RegisterSubmitted(var0);
});
+ } else if ($ === "tasks") {
+ return task_list_view(model);
} else {
return dashboard_view(model);
}
if ($ instanceof Ok) {
let value3 = $[0];
return [
- new Model("dashboard", 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),
none()
];
} else {
let _pipe = new NewTask(field2(fields, "title"), field2(fields, "description"), field2(fields, "assignee"), field2(fields, "assignees"), model.task_subteams, field2(fields, "due_date"), (() => {
let _pipe2 = key_find(fields, "defcon");
let _pipe$1 = unwrap2(_pipe2, "");
- let _pipe$2 = identity(_pipe$1);
- let _pipe$3 = run(_pipe$2, int2);
- return unwrap2(_pipe$3, 0);
+ let _pipe$2 = parse_int(_pipe$1);
+ return unwrap2(_pipe$2, 0);
})());
_block = new_task_json(_pipe);
let body2 = _block;
})());
}))
];
- } else {
+ } else if (message2 instanceof UnarchiveProject) {
let project_id = message2[0];
return [
model,
})());
}))
];
+ } 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),
+ 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),
+ none()
+ ];
}
}
function current_path() {
_block = "login";
} else if ($ === "/register") {
_block = "register";
+ } else if ($ === "/tasks") {
+ _block = "tasks";
} else {
_block = "dashboard";
}
return new DashboardLoaded(var0);
}))
];
+ } else if (page === "tasks") {
+ 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", 22, "main", "Pattern match failed, no pattern matched the value.", { value: $, start: 508, end: 558, pattern_start: 519, pattern_end: 524 });
+ 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 });
}
return;
}