step for pin code login

This commit is contained in:
Lett Osprey 2026-03-29 23:25:06 +02:00
parent e6851255dc
commit 0024687621
4 changed files with 118 additions and 46 deletions

View file

@ -1,11 +1,13 @@
import gleam/int
import gleam/json import gleam/json
import gleam/list import gleam/list
import gleam/result import gleam/result
import lustre import lustre
import lustre/attribute import lustre/attribute.{class}
import lustre/effect.{type Effect} import lustre/effect.{type Effect}
import lustre/element.{type Element} import lustre/element.{type Element}
import lustre/element/html import lustre/element/html
import lustre/event
import plinth/browser/document import plinth/browser/document
import plinth/browser/element as plinth_element import plinth/browser/element as plinth_element
import shared import shared
@ -27,10 +29,10 @@ pub fn main() {
} }
type Model { type Model {
Model(rooms: List(String), name: String) Model(rooms: List(shared.Room), name: String)
} }
fn init(items: List(String)) -> #(Model, Effect(Msg)) { fn init(items: List(shared.Room)) -> #(Model, Effect(Msg)) {
let model = Model(rooms: items, name: "") let model = Model(rooms: items, name: "")
#(model, effect.none()) #(model, effect.none())
@ -43,41 +45,85 @@ type Msg {
fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
case msg { case msg {
UserAddedItem -> { UserAddedItem ->
case model.name { case model.name {
"" -> #(model, effect.none()) _ -> #(model, effect.none())
name -> {
let updated_items = [name, ..model.rooms]
#(Model(rooms: updated_items, name: ""), effect.none())
}
} }
UserTypedNewItem(text) -> {
echo "CLICK " <> text <> "aa"
#(Model(..model, name: text), effect.none())
} }
UserTypedNewItem(text) -> #(Model(..model, name: text), effect.none())
} }
} }
fn view(model: Model) -> Element(Msg) { fn view(model: Model) -> Element(Msg) {
let styles = [ echo "NAME " <> model.name
#("max-width", "30ch"), case model.name {
#("margin", "0 auto"), "" ->
#("display", "flex"), html.div([], [
#("flex-direction", "column"), html.div([class("terminal-header")], [
#("gap", "1em"), html.div([class("terminal-status")], [
] html.span([class("status-blink")], [html.text("")]),
html.text(" SYSTEM READY"),
html.div([attribute.styles(styles)], [ html.span([class("ml-8")], [
html.h1([], [html.text("Select your QuizRoom")]), html.text("<< Please Log On to use QuizTerm. >>"),
view_room_list(model.rooms), ]),
]) ]),
} ]),
view_room_list(model.rooms),
fn view_room_list(items: List(String)) -> Element(Msg) { ])
case items {
[] -> html.p([], [html.text("No items in your list yet.")])
_ -> { _ -> {
html.ul([], list.map(items, fn(item) { html.li([], [html.text(item)]) })) html.div([], [
html.div([class("terminal-header")], [
html.div([class("terminal-status")], [
html.span([class("status-blink")], [html.text("")]),
html.text(" SYSTEM READY"),
html.span([class("ml-8")], [
html.text("<< Please Log On to use QuizTerm. >>"),
]),
]),
]),
pin(),
])
} }
} }
} }
fn pin() -> Element(Msg) {
html.div([attribute.class("terminal-section")], [
html.div([attribute.class("terminal-label mb-4")], [
html.text("Select room to play in"),
]),
html.div([attribute.class("participants-grid")], [
html.text("Enter PIN code for room"),
]),
])
}
fn view_room_list(items: List(shared.Room)) -> Element(Msg) {
html.div([attribute.class("terminal-section")], [
html.div([attribute.class("terminal-label mb-4")], [
html.text("Select room to play in"),
]),
html.div([attribute.class("participants-grid")], case items {
[] -> [html.text("No items in your list yet.")]
_ -> {
list.index_map(items, fn(item, index) {
content_cell(index, item, UserTypedNewItem)
})
}
}),
])
}
fn content_cell(
number: Int,
room: shared.Room,
on_click: fn(String) -> Msg,
) -> Element(Msg) {
html.div([class("participant-login"), event.on_click(on_click(room.name))], [
html.div([class("participant-name")], [
html.text("" <> "[#" <> int.to_string(number) <> "] Team " <> room.name),
]),
])
}

View file

@ -1,19 +1,16 @@
import gleam/dynamic/decode import gleam/dynamic/decode
import gleam/json
pub type GroceryItem { pub type Room {
GroceryItem(name: String, quantity: Int) Room(id: String, name: String, pin: String)
} }
pub fn grocery_list_decoder() -> decode.Decoder(List(String)) {
decode.list(decode.string)
}
fn grocery_item_to_json(grocery_item: GroceryItem) -> json.Json { pub fn grocery_list_decoder() -> decode.Decoder(List(Room)) {
let GroceryItem(name:, quantity:) = grocery_item let room_decoder = {
json.object([#("name", json.string(name)), #("quantity", json.int(quantity))]) use name <- decode.field("name", decode.string)
} use id <- decode.field("id", decode.string)
use pin <- decode.field("key", decode.string)
pub fn grocery_list_to_json(items: List(GroceryItem)) -> json.Json { decode.success(Room(id:, name:, pin: ))
json.array(items, grocery_item_to_json) }
decode.list(room_decoder)
} }

View file

@ -121,6 +121,12 @@ body {
transition: all 0.2s; transition: all 0.2s;
} }
.participant-login {
border: 2px dashed #005500;
padding: 1rem;
transition: all 0.2s;
}
.participant-hidden { .participant-hidden {
border: 0px dashed #005500; border: 0px dashed #005500;
padding: 1rem; padding: 1rem;
@ -139,6 +145,12 @@ body {
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3); box-shadow: 0 0 20px rgba(0, 255, 0, 0.3);
} }
.participant-login:hover {
border: 2px solid #00ff00;
background: rgba(0, 255, 0, 0.05);
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3);
}
.participant-name { .participant-name {
color: #00ff00; color: #00ff00;
font-weight: bold; font-weight: bold;

View file

@ -7,9 +7,26 @@
<script src="/client.js" type="module"></script> <script src="/client.js" type="module"></script>
<script id="model" type="application/json"> <script id="model" type="application/json">
[ [
"a", {
"b", "id": "abt",
"c" "name": "Billettering",
"key": "T5X6"
},
{
"id": "slg",
"name": "Salg",
"key": "6B4T"
},
{
"id": "prs",
"name": "Personalisering",
"key": "P2Q5"
},
{
"id": "srp",
"name": "Support",
"key": "P2Q5"
}
] ]
</script> </script>
<link href="/static/layout.css" rel="stylesheet" type="text/css"> <link href="/static/layout.css" rel="stylesheet" type="text/css">