]> git.taranathan.com Git - robotics-task-manager.git/commitdiff
defcon first
authorTaran Nathan <moogoesmeow123@gmail.com>
Sat, 26 Sep 2026 15:42:05 +0000 (08:42 -0700)
committerTaran Nathan <moogoesmeow123@gmail.com>
Sun, 27 Sep 2026 00:39:11 +0000 (17:39 -0700)
client/src/client.gleam
server/db/init.sql
server/src/server.gleam
server/src/sql.gleam
server/src/sql/create_task.sql
server/src/sql/list_tasks.sql
server/static/client.js
shared/src/shared.gleam

index 53923e8d0b5aab30ffa8f81359a526f48b4f7854..64184da853ff4f82ee8f14f94804652e89fd74c5 100644 (file)
@@ -1,3 +1,5 @@
+import gleam/dynamic
+import gleam/dynamic/decode
 import gleam/int
 import gleam/json
 import gleam/list
@@ -209,6 +211,11 @@ fn update(model: Model, message: Message) -> #(Model, Effect(Message)) {
           field(fields, "assignees"),
           model.task_subteams,
           field(fields, "due_date"),
+          list.key_find(fields, "defcon")
+            |> result.unwrap("")
+            |> dynamic.string
+            |> decode.run(decode.int)
+            |> result.unwrap(0),
         )
         |> shared.new_task_json
       #(
@@ -701,6 +708,7 @@ fn project_view(model: Model, project: shared.Project) -> Element(Message) {
               text_input("assignee", "Owner"),
               text_input("assignees", "Assignees"),
               text_input("due_date", "Due (MM/DD/YY)"),
+              text_input("defcon", "DEFCON"),
               subteam_checkboxes(model.task_subteams),
               html.textarea(
                 [
@@ -850,6 +858,10 @@ fn task_view(task: shared.Task) -> Element(Message) {
         html.small([], [html.text("Subteams")]),
         html.text(if_blank(join_subteams(task.subteams), "No subteam")),
       ]),
+      html.div([attribute.class("task-person")], [
+        html.small([], [html.text("DEFCON")]),
+        html.text(if_blank(int.to_string(task.defcon), "0")),
+      ]),
       html.div([attribute.class("task-due")], [
         html.small([], [html.text("Due")]),
         html.text(case task.due_date {
index 92900bbf262286432312c2aa9a8a728ecb59d88e..015299d998cb7e897be82de17384e3b9f5de99c8 100644 (file)
@@ -25,7 +25,8 @@ create table if not exists task (
   assignees text not null default '',
   subteams subteam[] not null default '{}'::subteam[],
   due_date date,
-  created_at timestamptz not null default now()
+  created_at timestamptz not null default now(),
+  defcon int not null default 0
 );
 
 alter table task add column if not exists assignees text not null default '';
index 95b76b509a9090ec6f519b79a30b24ce9cc824ac..4014b498443bdb052599b7fc0c0912c15a40ac99 100644 (file)
@@ -303,6 +303,7 @@ fn add_task(
           task.assignees,
           list.map(task.subteams, to_sql_subteam),
           task.due_date,
+          task.defcon,
         )
       wisp.json_response("{\"ok\":true}", 201)
     }
@@ -362,6 +363,7 @@ fn to_shared_task(task: sql.ListTasksRow) -> shared.Task {
     assignees: task.assignees,
     subteams: string.split(task.subteams, ","),
     due_date: task.due_date,
+    defcon: task.defcon,
   )
 }
 
index 2d5ee9afe8b5b5492acab8c2ffd1e910a8e3dfd4..ba967e2e24cb77225c8ba00d2460226f7c392403 100644 (file)
@@ -118,6 +118,7 @@ pub fn create_task(
   arg_5: String,
   arg_6: List(Subteam),
   arg_7: String,
+  arg_8: Int,
 ) -> Result(pog.Returned(CreateTaskRow), pog.QueryError) {
   let decoder = {
     use id <- decode.field(0, decode.int)
@@ -131,9 +132,10 @@ pub fn create_task(
   assignee,
   assignees,
   subteams,
-  due_date
+  due_date,
+  defcon
 )
-values ($1, $2, $3, $4, $5, $6::subteam[], nullif($7, '')::date)
+values ($1, $2, $3, $4, $5, $6::subteam[], nullif($7, '')::date, $8)
 returning id
 "
   |> pog.query
@@ -144,6 +146,7 @@ returning id
   |> pog.parameter(pog.text(arg_5))
   |> pog.parameter(pog.array(fn(value) { subteam_encoder(value) }, arg_6))
   |> pog.parameter(pog.text(arg_7))
+  |> pog.parameter(pog.int(arg_8))
   |> pog.returning(decoder)
   |> pog.execute(db)
 }
@@ -386,6 +389,7 @@ pub type ListTasksRow {
     status: String,
     assignee: String,
     assignees: String,
+    defcon: Int,
     subteams: String,
     due_date: String,
   )
@@ -409,8 +413,9 @@ pub fn list_tasks(
     use status <- decode.field(4, decode.string)
     use assignee <- decode.field(5, decode.string)
     use assignees <- decode.field(6, decode.string)
-    use subteams <- decode.field(7, decode.string)
-    use due_date <- decode.field(8, decode.string)
+    use defcon <- decode.field(7, decode.int)
+    use subteams <- decode.field(8, decode.string)
+    use due_date <- decode.field(9, decode.string)
     decode.success(ListTasksRow(
       id:,
       project_id:,
@@ -419,6 +424,7 @@ pub fn list_tasks(
       status:,
       assignee:,
       assignees:,
+      defcon:,
       subteams:,
       due_date:,
     ))
@@ -432,6 +438,7 @@ pub fn list_tasks(
   status,
   assignee,
   assignees,
+  defcon,
   coalesce(array_to_string(subteams, ','), '') as subteams,
   coalesce(due_date::text, '') as due_date
 from task
index 12c089a699ee56e4bffe40dd3a195ecb708fce87..a4a9c6a402e9b98726673033f74ed977daf425bc 100644 (file)
@@ -5,7 +5,8 @@ insert into task (
   assignee,
   assignees,
   subteams,
-  due_date
+  due_date,
+  defcon
 )
-values ($1, $2, $3, $4, $5, $6::subteam[], nullif($7, '')::date)
+values ($1, $2, $3, $4, $5, $6::subteam[], nullif($7, '')::date, $8)
 returning id
index 27b08074413c9f68b6fe1c39a932c14f23258328..6fd52e2afb8c8729bbea60e1550c87dddbd63063 100644 (file)
@@ -6,6 +6,7 @@ select
   status,
   assignee,
   assignees,
+  defcon,
   coalesce(array_to_string(subteams, ','), '') as subteams,
   coalesce(due_date::text, '') as due_date
 from task
index 84839b5727937bbb709fe89d69f1444bfb013a5b..f92bb977386af363e7e33d7abc7b5c011215232d 100644 (file)
@@ -2170,6 +2170,9 @@ function string4(input) {
 function bool2(input) {
   return identity2(input);
 }
+function int3(input) {
+  return identity2(input);
+}
 function object2(entries) {
   return object(entries);
 }
@@ -7127,7 +7130,7 @@ class Project extends CustomType {
   }
 }
 class Task extends CustomType {
-  constructor(id, project_id, title2, description, status, assignee, assignees, subteams, due_date) {
+  constructor(id, project_id, title2, description, status, assignee, assignees, subteams, due_date, defcon) {
     super();
     this.id = id;
     this.project_id = project_id;
@@ -7138,6 +7141,7 @@ class Task extends CustomType {
     this.assignees = assignees;
     this.subteams = subteams;
     this.due_date = due_date;
+    this.defcon = defcon;
   }
 }
 class Dashboard extends CustomType {
@@ -7163,7 +7167,7 @@ class NewProject extends CustomType {
   }
 }
 class NewTask extends CustomType {
-  constructor(title2, description, assignee, assignees, subteams, due_date) {
+  constructor(title2, description, assignee, assignees, subteams, due_date, defcon) {
     super();
     this.title = title2;
     this.description = description;
@@ -7171,6 +7175,7 @@ class NewTask extends CustomType {
     this.assignees = assignees;
     this.subteams = subteams;
     this.due_date = due_date;
+    this.defcon = defcon;
   }
 }
 class StatusChange extends CustomType {
@@ -7189,7 +7194,9 @@ function task_decoder() {
               return field("assignees", string3, (assignees) => {
                 return field("subteams", list2(string3), (subteams) => {
                   return field("due_date", string3, (due_date) => {
-                    return success(new Task(id, project_id, title2, description, status, assignee, assignees, subteams, due_date));
+                    return field("defcon", int2, (defcon) => {
+                      return success(new Task(id, project_id, title2, description, status, assignee, assignees, subteams, due_date, defcon));
+                    });
                   });
                 });
               });
@@ -7240,7 +7247,8 @@ function new_task_json(value3) {
     ["assignee", string4(value3.assignee)],
     ["assignees", string4(value3.assignees)],
     ["subteams", array2(value3.subteams, string4)],
-    ["due_date", string4(value3.due_date)]
+    ["due_date", string4(value3.due_date)],
+    ["defcon", int3(value3.defcon)]
   ]));
 }
 function status_change_json(value3) {
@@ -7515,9 +7523,6 @@ function left_status_button(task) {
     return none2();
   }
 }
-function join_subteams(subteams) {
-  return join(subteams, ", ");
-}
 function if_blank(value3, fallback) {
   let $ = value3 === "";
   if ($) {
@@ -7526,6 +7531,9 @@ function if_blank(value3, fallback) {
     return value3;
   }
 }
+function join_subteams(subteams) {
+  return join(subteams, ", ");
+}
 function task_view(task) {
   return div(toList([class$("task")]), toList([
     div(toList([class$("task-header")]), toList([
@@ -7552,6 +7560,10 @@ function task_view(task) {
         small(List$Empty$const, toList([text3("Subteams")])),
         text3(if_blank(join_subteams(task.subteams), "No subteam"))
       ])),
+      div(toList([class$("task-person")]), toList([
+        small(List$Empty$const, toList([text3("DEFCON")])),
+        text3(if_blank(to_string(task.defcon), "0"))
+      ])),
       div(toList([class$("task-due")]), toList([
         small(List$Empty$const, toList([text3("Due")])),
         text3((() => {
@@ -7639,6 +7651,7 @@ function project_view(model, project) {
       text_input("assignee", "Owner"),
       text_input("assignees", "Assignees"),
       text_input("due_date", "Due (MM/DD/YY)"),
+      text_input("defcon", "DEFCON"),
       subteam_checkboxes(model.task_subteams),
       textarea(toList([
         class$("task-description-input"),
@@ -7926,7 +7939,7 @@ function update_task_status(model, task_id, status) {
       let tasks = map2(project.tasks, (task) => {
         let $1 = task.id === task_id;
         if ($1) {
-          return new Task(task.id, task.project_id, task.title, task.description, status, task.assignee, task.assignees, task.subteams, task.due_date);
+          return new Task(task.id, task.project_id, task.title, task.description, status, task.assignee, task.assignees, task.subteams, task.due_date, task.defcon);
         } else {
           return task;
         }
@@ -8026,7 +8039,13 @@ function update2(model, message2) {
     let project_id = message2[0];
     let fields = message2[1];
     let _block;
-    let _pipe = new NewTask(field2(fields, "title"), field2(fields, "description"), field2(fields, "assignee"), field2(fields, "assignees"), model.task_subteams, field2(fields, "due_date"));
+    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);
+    })());
     _block = new_task_json(_pipe);
     let body2 = _block;
     return [
@@ -8270,7 +8289,7 @@ function main2() {
   let app = application(init, update2, view);
   let $ = start4(app, "#app", Option$None$const);
   if (!($ instanceof Ok)) {
-    throw makeError("let_assert", FILEPATH, "client", 20, "main", "Pattern match failed, no pattern matched the value.", { value: $, start: 459, end: 509, pattern_start: 470, pattern_end: 475 });
+    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;
 }
index ec8f0de56184779c5a5fdc50bddf451bd63b00b4..378cc21f062b2aaf93d176f7ef060270d97ed03d 100644 (file)
@@ -22,6 +22,7 @@ pub type Task {
     assignees: String,
     subteams: List(String),
     due_date: String,
+    defcon: Int,
   )
 }
 
@@ -45,6 +46,7 @@ pub type NewTask {
     assignees: String,
     subteams: List(String),
     due_date: String,
+    defcon: Int,
   )
 }
 
@@ -77,7 +79,19 @@ fn task_decoder() -> decode.Decoder(Task) {
   use assignees <- decode.field("assignees", decode.string)
   use subteams <- decode.field("subteams", decode.list(decode.string))
   use due_date <- decode.field("due_date", decode.string)
-  decode.success(Task(id:, project_id:, title:, description:, status:, assignee:, assignees:, subteams:, due_date:))
+  use defcon <- decode.field("defcon", decode.int)
+  decode.success(Task(
+    id:,
+    project_id:,
+    title:,
+    description:,
+    status:,
+    assignee:,
+    assignees:,
+    subteams:,
+    due_date:,
+    defcon:,
+  ))
 }
 
 pub fn credentials_decoder() -> decode.Decoder(Credentials) {
@@ -100,7 +114,16 @@ pub fn new_task_decoder() -> decode.Decoder(NewTask) {
   use assignees <- decode.field("assignees", decode.string)
   use subteams <- decode.field("subteams", decode.list(decode.string))
   use due_date <- decode.field("due_date", decode.string)
-  decode.success(NewTask(title:, description:, assignee:, assignees:, subteams:, due_date:))
+  use defcon <- decode.field("defcon", decode.int)
+  decode.success(NewTask(
+    title:,
+    description:,
+    assignee:,
+    assignees:,
+    subteams:,
+    due_date:,
+    defcon:,
+  ))
 }
 
 pub fn status_change_decoder() -> decode.Decoder(StatusChange) {
@@ -137,6 +160,7 @@ fn task_json(value: Task) -> json.Json {
     #("assignees", json.string(value.assignees)),
     #("subteams", json.array(value.subteams, json.string)),
     #("due_date", json.string(value.due_date)),
+    #("defcon", json.int(value.defcon)),
   ])
 }
 
@@ -163,6 +187,7 @@ pub fn new_task_json(value: NewTask) -> json.Json {
     #("assignees", json.string(value.assignees)),
     #("subteams", json.array(value.subteams, json.string)),
     #("due_date", json.string(value.due_date)),
+    #("defcon", json.int(value.defcon)),
   ])
 }