chore: pc change

This commit is contained in:
alphafetus committed 2025-08-26 09:57:58 -04:00
1 parent 21ba5e45ff
commit f7ca1ecbc5
27 files changed
+926 -262

No files matched your search

Generated
+436 -18
View File
@@ -26,6 +26,21 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "any_spawner" name = "any_spawner"
version = "0.3.0" version = "0.3.0"
@@ -565,7 +580,7 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
"object", "object",
"rustc-demangle", "rustc-demangle",
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@@ -688,6 +703,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
[[package]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "1.8.1" version = "1.8.1"
@@ -858,6 +888,41 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "6.1.0" version = "6.1.0"
@@ -878,6 +943,18 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "default-struct-builder"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0df63c21a4383f94bd5388564829423f35c316aed85dc4f8427aded372c7c0d"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.4.0" version = "0.4.0"
@@ -1134,8 +1211,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
] ]
[[package]] [[package]]
@@ -1482,6 +1561,30 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
version = "1.5.0" version = "1.5.0"
@@ -1600,6 +1703,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.0.3" version = "1.0.3"
@@ -1657,6 +1766,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.5" version = "0.10.5"
@@ -1701,6 +1816,19 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "jsonwebtoken"
version = "9.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
dependencies = [
"base64 0.22.1",
"js-sys",
"ring",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@@ -1734,7 +1862,7 @@ dependencies = [
"oco_ref", "oco_ref",
"or_poisoned", "or_poisoned",
"paste", "paste",
"rand", "rand 0.9.2",
"reactive_graph", "reactive_graph",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"rustc_version", "rustc_version",
@@ -1755,6 +1883,26 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "leptos-use"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6eac17e7d306b4ad67158aba97c1490884ba304add4321069cb63fe0834c3b1"
dependencies = [
"cfg-if",
"codee",
"default-struct-builder",
"js-sys",
"lazy_static",
"leptos",
"paste",
"send_wrapper",
"thiserror 2.0.12",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "leptos_axum" name = "leptos_axum"
version = "0.8.5" version = "0.8.5"
@@ -1878,6 +2026,26 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "leptos_oidc"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7797ff727c5b0b558faabe2bd784159b411803f56e384578d68471c699b85649"
dependencies = [
"chrono",
"codee",
"jsonwebtoken",
"leptos",
"leptos-use",
"leptos_router",
"oauth2",
"reqwest",
"serde",
"serde_json",
"thiserror 2.0.12",
"tracing",
]
[[package]] [[package]]
name = "leptos_router" name = "leptos_router"
version = "0.8.5" version = "0.8.5"
@@ -1948,7 +2116,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets", "windows-targets 0.53.3",
] ]
[[package]] [[package]]
@@ -1985,6 +2153,14 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "macros"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "manyhow" name = "manyhow"
version = "0.11.4" version = "0.11.4"
@@ -2129,6 +2305,25 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "oauth2"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d"
dependencies = [
"base64 0.22.1",
"chrono",
"getrandom 0.2.16",
"http 1.3.1",
"rand 0.8.5",
"serde",
"serde_json",
"serde_path_to_error",
"sha2",
"thiserror 1.0.69",
"url",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.7" version = "0.36.7"
@@ -2198,7 +2393,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@@ -2368,14 +2563,35 @@ version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.2" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [ dependencies = [
"rand_chacha", "rand_chacha 0.9.0",
"rand_core", "rand_core 0.9.3",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
] ]
[[package]] [[package]]
@@ -2385,7 +2601,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core", "rand_core 0.9.3",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
] ]
[[package]] [[package]]
@@ -2493,6 +2718,42 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [
"base64 0.22.1",
"bytes",
"futures-core",
"futures-util",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"once_cell",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-registry",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@@ -2693,9 +2954,12 @@ dependencies = [
"leptos", "leptos",
"leptos_axum", "leptos_axum",
"leptos_meta", "leptos_meta",
"leptos_oidc",
"leptos_router", "leptos_router",
"macros",
"serde", "serde",
"tokio", "tokio",
"url",
"uuid", "uuid",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -2999,6 +3263,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@@ -3033,6 +3303,9 @@ name = "sync_wrapper"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "synstructure" name = "synstructure"
@@ -3377,7 +3650,7 @@ dependencies = [
"http 1.3.1", "http 1.3.1",
"httparse", "httparse",
"log", "log",
"rand", "rand 0.9.2",
"sha1", "sha1",
"thiserror 2.0.12", "thiserror 2.0.12",
"utf-8", "utf-8",
@@ -3448,6 +3721,7 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]] [[package]]
@@ -3678,13 +3952,92 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings 0.4.2",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-registry"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-strings 0.3.1",
"windows-targets 0.53.3",
]
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@@ -3693,7 +4046,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ dependencies = [
"windows-targets", "windows-targets 0.52.6",
] ]
[[package]] [[package]]
@@ -3702,14 +4055,31 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu", "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm", "windows_i686_gnullvm 0.52.6",
"windows_i686_msvc", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc", "windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
] ]
[[package]] [[package]]
@@ -3718,48 +4088,96 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.12" version = "0.7.12"
+4
View File
@@ -19,6 +19,10 @@ aws-sdk-dynamodb = { version = "1.74.0", optional = true }
aws-config = { version = "1.6.2", optional = true } aws-config = { version = "1.6.2", optional = true }
serde = "1.0.219" serde = "1.0.219"
uuid = { version = "1.17.0", features = ["serde", "v4", "js"] } uuid = { version = "1.17.0", features = ["serde", "v4", "js"] }
leptos_oidc = "0.9.0"
url = "2.5.4"
macros = { path = "./macros" }
[features] [features]
hydrate = [ hydrate = [
+16 -86
View File
@@ -1,91 +1,21 @@
<picture> # Region 15 Scholarship Application (Rust Rewrite)
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
</picture>
# Leptos Axum Starter Template ## Requirements
This repo requires that you have `cargo`, `clang`, and `node` installed.
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum). - `cargo`: the project is written in Rust.
- `clang`: `ring`, a dependency of the `leptos_oidc` crate, requires this for compilation.
- `node`: required for `tailwindcss` compilation.
## Creating your template repo ## How to run
Run the command `cargo leptos watch` to compile and run an instance of the server on your local machine. To access info
from the AWS resources, you will need to be approved by me or someone else with AWS administrator access to obtain a
usable auth key from AWS IAM.
If you don't have `cargo-leptos` installed you can install it with ## Current Tasks
There are several tasks left to complete. These are a few of the larger ones:
```bash - Build the application's custom elements
cargo install cargo-leptos --locked - Build out the database requests
``` - Create proper authentication
- PDF generation using data (server-side)
Then run
```bash
cargo leptos new --git https://github.com/leptos-rs/start-axum
```
to generate a new project template.
```bash
cd scholarships-rs
```
to go to your newly created project.
Feel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.
Addtionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.
## Running your project
```bash
cargo leptos watch
```
## Installing Additional Tools
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
4. `npm install -g sass` - install `dart-sass` (should be optional in future
5. Run `npm install` in end2end subdirectory before test
## Compiling for Release
```bash
cargo leptos build --release
```
Will generate your server binary in target/server/release and your site package in target/site
## Testing Your Project
```bash
cargo leptos end-to-end
```
```bash
cargo leptos end-to-end --release
```
Cargo-leptos uses Playwright as the end-to-end test tool.
Tests are located in end2end/tests directory.
## Executing a Server on a Remote Machine Without the Toolchain
After running a `cargo leptos build --release` the minimum files needed are:
1. The server binary located in `target/server/release`
2. The `site` directory and all files within located in `target/site`
Copy these files to your remote server. The directory structure should be:
```text
scholarships-rs
site/
```
Set the following environment variables (updating for your project as needed):
```sh
export LEPTOS_OUTPUT_NAME="scholarships-rs"
export LEPTOS_SITE_ROOT="site"
export LEPTOS_SITE_PKG_DIR="pkg"
export LEPTOS_SITE_ADDR="127.0.0.1:3000"
export LEPTOS_RELOAD_PORT="3001"
```
Finally, run the server binary.
## Licensing
This template itself is released under the Unlicense. You should replace the LICENSE for your own application with an appropriate license if you plan to release it publicly.
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "macros"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
syn = "2.0"
quote = "1.0"
+78
View File
@@ -0,0 +1,78 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, Ident, parse_macro_input};
#[proc_macro_derive(Reactive)]
pub fn make_reactive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::DeriveInput);
let name = input.ident;
let reactive_name = Ident::new(&format!("{}Reactive", name), name.span());
let fields = if let Data::Struct(data) = input.data {
data.fields
} else {
panic!("Only structs can be reactive")
};
// Build the new fields.
let mut new_fields = Vec::new();
let mut new_fn_args = Vec::new();
let mut from_fn_args = Vec::new();
for field in fields {
let field_name = field.ident.unwrap();
let field_type = field.ty;
let new_field = quote! {
pub #field_name: _RwSignal<#field_type>,
};
new_fields.push(new_field);
// Create new function arguments/inits
let new_fn_arg = quote! {
#field_name: _RwSignal::new(data.#field_name),
};
let from_fn_arg = quote! {
#field_name: self.#field_name.get_untracked(),
};
new_fn_args.push(new_fn_arg);
from_fn_args.push(from_fn_arg);
}
let reactive_docs = format!("Reactive version of the [`{name}`] struct. Automatically created by the `Reactive` macro.");
// Expand the macro. The output is a struct with new reactive fields and a new function,
// which initializes the reactive fields based on the original struct's fields.
let expanded = quote! {
use leptos::prelude::RwSignal as _RwSignal; // in case the user imports it themselves
use leptos::prelude::GetUntracked as _;
#[doc = #reactive_docs]
pub struct #reactive_name {
#(#new_fields)*
}
impl #reactive_name {
/// Creates a new `#reactive_name` struct from a non-reactive `#name`.
pub fn new(data: #name) -> Self {
Self {
#(#new_fn_args)*
}
}
/// Captures the information of this struct into a `#name` struct, for use
/// in server functions.
pub fn capture(&self) -> #name {
#name {
#(#from_fn_args)*
}
}
}
};
TokenStream::from(expanded)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+2
View File
@@ -0,0 +1,2 @@
[rustfmt]
overrideCommand = ["leptosfmt", "--stdin", "--rustfmt"]
+1
View File
@@ -0,0 +1 @@
edition = "2021"
+46 -5
View File
@@ -1,15 +1,15 @@
use leptos::prelude::*; use leptos::prelude::*;
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title}; use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
use leptos_oidc::{
Auth, AuthLoaded, AuthParameters, AuthSignal, Authenticated, Challenge, LoginLink, LogoutLink,
};
use leptos_router::{ use leptos_router::{
components::{Route, Router, Routes}, components::{Route, Router, Routes},
StaticSegment, StaticSegment,
}; };
use url::Url;
use crate::pages::{ use crate::pages::{AboutPage, HomePage, ProviderEntry};
AboutPage,
HomePage,
ProviderEntry
};
pub fn shell(options: LeptosOptions) -> impl IntoView { pub fn shell(options: LeptosOptions) -> impl IntoView {
view! { view! {
@@ -34,17 +34,38 @@ pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc. // Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context(); provide_meta_context();
// Authentication setup
let parameters = AuthParameters {
issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_Lfjuy5zaM".into(),
client_id: "10jr2h3vtpu9n7gj46pvg5qo2q".into(),
redirect_uri: Url::parse("http://localhost:3000").unwrap().to_string(),
post_logout_redirect_uri: Url::parse("http://localhost:3000").unwrap().to_string(),
scope: Some("openid%20profile%20email".into()),
audience: None,
challenge: Challenge::None,
};
let auth = Auth::signal();
provide_context(auth); // allows use of this signal in lower areas of the tree without
// explicitly passing it through the html tree
let _ = Auth::init(parameters);
view! { view! {
// injects a stylesheet into the document <head> // injects a stylesheet into the document <head>
// id=leptos means cargo-leptos will hot-reload this stylesheet // id=leptos means cargo-leptos will hot-reload this stylesheet
<Stylesheet id="leptos" href="/pkg/scholarships-rs-wasm.css"/> <Stylesheet id="leptos" href="/pkg/scholarships-rs-wasm.css"/>
// <AuthLoading><p>"Authentication is loading"</p></AuthLoading>
// <AuthErrorContext><AuthErrorView/></AuthErrorContext>
// sets the document title // sets the document title
<Title text="Welcome to Leptos"/> <Title text="Welcome to Leptos"/>
// content for this welcome page // content for this welcome page
<Router> <Router>
<main> <main>
// TODO Create a 404 page
<Routes fallback=|| "Page not found.".into_view()> <Routes fallback=|| "Page not found.".into_view()>
<Route path=StaticSegment("") view=HomePage/> <Route path=StaticSegment("") view=HomePage/>
<Route path=StaticSegment("about") view=AboutPage/> <Route path=StaticSegment("about") view=AboutPage/>
@@ -54,3 +75,23 @@ pub fn App() -> impl IntoView {
</Router> </Router>
} }
} }
#[component]
pub fn Unauthenticated() -> impl IntoView {
view! {
<p>"Error 401: Unauthorized"</p>
<LoginLink class="text-login">"Sign in"</LoginLink>
}
}
#[component]
pub fn AuthErrorView() -> impl IntoView {
let auth = use_context::<AuthSignal>().expect("AuthError: RwSignal<AuthStore> was not found");
let error_message = move || auth.get().error().map(|error| format!("{error:?}"));
view! {
<h1>"An authentication error occurred."</h1>
{error_message}
}
}
+6 -9
View File
@@ -1,15 +1,14 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct FormDefinition { struct FormDefinition {
sections: Vec<Section> sections: Vec<Section>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct Section { struct Section {
questions: Vec<QuestionType> questions: Vec<QuestionType>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@@ -20,12 +19,10 @@ enum QuestionType {
Dropdown, Dropdown,
Checkbox, Checkbox,
Radio, Radio,
Multi Multi,
} }
struct TextInput { struct TextInput {}
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct QuestionComponent { struct QuestionComponent {
@@ -34,5 +31,5 @@ struct QuestionComponent {
placeholder: Option<String>, placeholder: Option<String>,
// Used in <select> and <checkbox> elements // Used in <select> and <checkbox> elements
options: Option<HashMap<String, String>> options: Option<HashMap<String, String>>,
} }
+2 -3
View File
@@ -1,10 +1,9 @@
#[derive(Clone)] #[derive(Clone)]
pub struct LoanerUser { pub struct LoanerUser {
pub first_name: String, pub first_name: String,
pub last_name: String, pub last_name: String,
pub email: String, pub email: String,
pub has_loan: bool pub has_loan: bool,
} }
impl LoanerUser { impl LoanerUser {
@@ -13,7 +12,7 @@ impl LoanerUser {
first_name: "".to_string(), first_name: "".to_string(),
last_name: "".to_string(), last_name: "".to_string(),
email: "".to_string(), email: "".to_string(),
has_loan: false has_loan: false,
} }
} }
} }
+4 -6
View File
@@ -1,8 +1,6 @@
mod loaner_user;
mod student_info;
mod form_definition; mod form_definition;
mod loaner_user;
mod student_info;
mod user;
pub use self::{ pub use self::{loaner_user::*, student_info::*, user::*};
loaner_user::*,
student_info::*
};
+4 -2
View File
@@ -1,7 +1,9 @@
use serde::{Deserialize, Serialize}; use macros::Reactive;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone, Reactive)]
pub struct StudentInfo { pub struct StudentInfo {
pub first_name: String, pub first_name: String,
pub last_name: String, pub last_name: String,
pub math_sat_score: i32,
} }
+25
View File
@@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
// API URL to get user information from Google's Groups API:
// https://admin.googleapis.com/admin/directory/v1/groups?domain=region15.org&userKey=<email>
// Note that this requires a service account of some type, and that it should only be called from
// a pre-sign up lambda function.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserClaims {
/// The time the JWT was issued, in UTC time.
#[serde(rename = "iat")]
issued_at: usize,
/// The issuer of the JWT.
#[serde(rename = "iss")]
issuer: String,
/// The time the JWT expires, in UTC time.
#[serde(rename = "exp")]
expiration_at: usize,
/// The subject of the JWT. This is the only unique identifier for a user.
#[serde(rename = "sub")]
pub subject: String,
/// The username of the user. This should be their email address.
#[serde(rename = "username")]
pub username: String,
}
+19
View File
@@ -0,0 +1,19 @@
use leptos::prelude::*;
#[component]
pub fn Checkbox(
#[prop()] checked: ReadSignal<bool>,
#[prop()] on_change: Callback<bool>,
#[prop()] label: String,
#[prop()] id: String
) -> impl IntoView {
let label_id = id.clone();
view! {
<label for=label_id>
<input
type="checkbox"
id={id}
/>
</label>
}
}
+10
View File
@@ -0,0 +1,10 @@
use leptos::prelude::*;
#[component]
pub fn Loading() -> impl IntoView {
view! {
<div>
<img src="loading.gif" />
</div>
}
}
+5 -6
View File
@@ -1,9 +1,8 @@
mod action_button; mod action_button;
mod text_field; mod loading;
mod multi_entry; mod multi_entry;
mod text_field;
mod select;
mod checkboxes;
pub use self::{ pub use self::{action_button::*, loading::*, multi_entry::*, text_field::*, select::*};
action_button::*,
text_field::*,
multi_entry::*
};
+14 -15
View File
@@ -1,9 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::components::ActionButton;
use leptos::{leptos_dom::logging::console_log, prelude::*}; use leptos::{leptos_dom::logging::console_log, prelude::*};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use crate::components::ActionButton;
#[derive(Clone, Serialize, Deserialize, Debug)] #[derive(Clone, Serialize, Deserialize, Debug)]
pub struct MultiEntryData { pub struct MultiEntryData {
@@ -27,7 +27,7 @@ pub struct MultiEntryMember {
/// The name of the member, used for display only. /// The name of the member, used for display only.
pub display_name: String, pub display_name: String,
/// The actual name of the member, used to get the data from an Entry struct. /// The actual name of the member, used to get the data from an Entry struct.
pub member_name: String pub member_name: String,
} }
impl MultiEntryMember { impl MultiEntryMember {
@@ -35,7 +35,7 @@ impl MultiEntryMember {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
display_name: String::new(), display_name: String::new(),
member_name: String::new() member_name: String::new(),
} }
} }
@@ -43,7 +43,7 @@ impl MultiEntryMember {
pub fn from(display_name: String, member_name: String) -> Self { pub fn from(display_name: String, member_name: String) -> Self {
Self { Self {
display_name, display_name,
member_name member_name,
} }
} }
@@ -51,7 +51,7 @@ impl MultiEntryMember {
pub fn from_str(display_name: &str, member_name: &str) -> Self { pub fn from_str(display_name: &str, member_name: &str) -> Self {
Self { Self {
display_name: display_name.into(), display_name: display_name.into(),
member_name: member_name.into() member_name: member_name.into(),
} }
} }
} }
@@ -85,9 +85,6 @@ pub fn Entry(
/// The secondary display member for this entry. /// The secondary display member for this entry.
#[prop()] #[prop()]
info_member: Signal<MultiEntryMember>, info_member: Signal<MultiEntryMember>,
/// The child components of this Entry view.
#[prop()]
children: Children
) -> impl IntoView { ) -> impl IntoView {
let nameData = entry_data.data.get(&name_member.get().member_name).unwrap(); let nameData = entry_data.data.get(&name_member.get().member_name).unwrap();
let infoData = entry_data.data.get(&info_member.get().member_name).unwrap(); let infoData = entry_data.data.get(&info_member.get().member_name).unwrap();
@@ -111,17 +108,19 @@ pub fn MultiEntry(
name_member: Signal<MultiEntryMember>, name_member: Signal<MultiEntryMember>,
/// The secondary display member for each entry object. /// The secondary display member for each entry object.
#[prop()] #[prop()]
info_member: Signal<MultiEntryMember> info_member: Signal<MultiEntryMember>,
) -> impl IntoView { ) -> impl IntoView {
// Creates an entry in the given entries list. // Creates an entry in the given entries list.
let create_entry = move |_| { let create_entry = move |_| {
let mut new_entry = MultiEntryData::new(); let mut new_entry = MultiEntryData::new();
new_entry new_entry.data.insert(
.data name_member.get().member_name,
.insert(name_member.get().member_name, ValueType::String(String::from("Testing"))); ValueType::String(String::from("Testing")),
new_entry );
.data new_entry.data.insert(
.insert(info_member.get().member_name, ValueType::String(new_entry.id.to_string())); info_member.get().member_name,
ValueType::String(new_entry.id.to_string()),
);
entries.update(|entry_vec| entry_vec.push(new_entry)); entries.update(|entry_vec| entry_vec.push(new_entry));
}; };
+38
View File
@@ -0,0 +1,38 @@
use leptos::prelude::*;
/// A custom-styled select input.
///
/// Takes a `Vec<String>` as the set of inputs. Only one value may be selected, which is indicated
/// by the `value` prop of the component.
#[component]
pub fn Select(
#[prop(default = vec!["Test Value".into()])] value_list: Vec<String>,
#[prop(default = RwSignal::new("".into()))] value: RwSignal<String>,
#[prop(default = RwSignal::new("".into()))] label: RwSignal<String>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
) -> impl IntoView {
view! {
<div>
<label>
{label}
<select
class="relative border-2 m-1.5 p-1.5 rounded-md"
class=(["border-red-700", "bg-transparent"], move || !disabled.get())
class=(["border-gray-600", "pointer-events-none", "bg-gray-600/33"], move || disabled.get())
on:change:target=move |e| {
value.set(e.target().value());
}>
// This closure handles the display of the options.
{move || {
value_list.iter().map(
move |value| {
let display = value.to_owned();
view! { <option value={display}>{display.clone()}</option> }
}
).collect::<Vec<_>>()
}}
</select>
</label>
</div>
}
}
+19 -6
View File
@@ -1,26 +1,39 @@
use std::str::FromStr;
use leptos::prelude::*; use leptos::prelude::*;
#[component] #[component]
pub fn OutlinedTextField( pub fn OutlinedTextField<T>(
#[prop(default = String::from(""))] placeholder: String, #[prop(default = String::from(""))] placeholder: String,
#[prop(default = RwSignal::new("".to_string()))] value: RwSignal<String>, #[prop(default = RwSignal::new(T::default()))] value: RwSignal<T>,
#[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>, #[prop(default = RwSignal::new(false))] disabled: RwSignal<bool>,
#[prop(default = String::from(""))] name: String, #[prop(default = String::from(""))] name: String,
#[prop(default = String::from(""))] label: String, #[prop(default = String::from(""))] label: String,
#[prop(default = String::from(""))] input_type: String, #[prop(default = String::from(""))] input_type: String,
) -> impl IntoView { ) -> impl IntoView
where
T: Default + 'static + FromStr + ToString + PartialEq + Send + Sync + Clone
{
// Parsing logic for non-string types which happens on each input.
let on_input = move |e| {
let input_value = event_target_value(&e);
let parsed_value = T::from_str(&input_value)
.unwrap_or_default();
value.set(parsed_value);
};
view! ( view! (
<label> <label>
{ label } { label }
<input <input
class="border-2 m-1.5 p-1.5 rounded-md bg-transparent mt-6 relative transition-all duration-400" class="border-2 m-1.5 p-1.5 rounded-md bg-transparent mt-6 relative transition-all duration-400"
class=(["border-red-700"], move || !disabled.get()) class=(["border-red-700", "bg-transparent"], move || !disabled.get())
class=(["border-gray-600", "pointer-events-none"], move || disabled.get()) class=(["border-gray-600", "pointer-events-none", "bg-gray-600/33"], move || disabled.get())
r#type={input_type} r#type={input_type}
disabled={disabled} disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
prop:name=name prop:name=name
bind:value=value // This is a two-way binding - it is allowed to read *and* write. prop:value={move || value.get().to_string()} // Sets the initial value.
on:input=on_input // Sets the value on each input.
/> />
</label> </label>
) )
+3 -1
View File
@@ -1,7 +1,9 @@
use leptos::prelude::event_target_value;
pub mod app; pub mod app;
mod common;
mod components; mod components;
mod pages; mod pages;
mod common;
#[cfg(feature = "hydrate")] #[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen] #[wasm_bindgen::prelude::wasm_bindgen]
+125 -68
View File
@@ -1,38 +1,54 @@
use crate::components::{ActionButton, OutlinedTextField}; use std::collections::HashMap;
use crate::common::StudentInfo;
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client}; use aws_sdk_dynamodb::{error::ProvideErrorMetadata, types::AttributeValue, Client};
use crate::app::Unauthenticated;
use crate::common::{StudentInfo, StudentInfoReactive, UserClaims};
use crate::components::{ActionButton, Loading, OutlinedTextField, Select};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated};
#[server(GetSubmission, endpoint = "/get-submission")] #[server(GetSubmission, endpoint = "/get-submission")]
pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> { pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
let dbclient = Client::new(&config); let dbclient = Client::new(&config);
console_log(format!("Getting values from API using key {}", id).as_str()); console_log(format!("Getting values from API using username {}", id).as_str());
match dbclient match dbclient
.get_item() .get_item()
.table_name("student-applications") .table_name("student-applications")
.key("Email", AttributeValue::S(id)) .key("Email", AttributeValue::S(id.clone()))
.send() .send()
.await .await
{ {
Ok(output) => { Ok(output) => {
console_log("Found submission with passed key.");
let item = output.item.unwrap(); let item = output.item.unwrap();
let first_name = item
let first_name: String = item
.get("studentFirstName") .get("studentFirstName")
.unwrap() .and_then(|attr| attr.as_s().ok())
.as_s() .map(|s| s.to_owned())
.unwrap() .unwrap_or_default();
.clone();
let last_name = item.get("studentLastName").unwrap().as_s().unwrap().clone(); let last_name: String = item
.get("studentLastName")
.and_then(|attr| attr.as_s().ok())
.map(|s| s.to_owned())
.unwrap_or_default();
let math_sat_score = item
.get("mathSAT")
.and_then(|attr| attr.as_n().ok())
.map(|n| n.parse::<i32>().unwrap_or(0))
.unwrap_or_default();
console_log(format!("Got values from API: {} {}", first_name, last_name).as_str());
Ok(StudentInfo { Ok(StudentInfo {
first_name, first_name,
last_name, last_name,
math_sat_score,
}) })
} }
Err(err) => { Err(err) => {
@@ -43,7 +59,10 @@ pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
} }
#[server(CreateSampleSubmission, endpoint = "/create-sample-submission")] #[server(CreateSampleSubmission, endpoint = "/create-sample-submission")]
pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), ServerFnError> { pub async fn create_sample_submission(
student_info: StudentInfo,
subject: String,
) -> Result<(), ServerFnError> {
use aws_sdk_dynamodb::{types::AttributeValue, Client}; use aws_sdk_dynamodb::{types::AttributeValue, Client};
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
@@ -51,18 +70,16 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
console_log( console_log(
format!( format!(
"Creating sample submission with name {} {}", "Creating sample submission with name {} {}, math score {}, and subject {}",
student_info.first_name, student_info.last_name student_info.first_name, student_info.last_name, student_info.math_sat_score, subject
) )
.as_str(), .as_str(),
); );
let submission_id = String::from("testing_student");
match dbclient match dbclient
.update_item() .update_item()
.table_name("student-applications") .table_name("student-applications")
.key("Email", AttributeValue::S(submission_id)) .key("Email", AttributeValue::S(subject))
.expression_attribute_values( .expression_attribute_values(
":studentFirstName", ":studentFirstName",
AttributeValue::S(student_info.first_name), AttributeValue::S(student_info.first_name),
@@ -71,10 +88,17 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
":studentLastName", ":studentLastName",
AttributeValue::S(student_info.last_name), AttributeValue::S(student_info.last_name),
) )
.expression_attribute_values(
":mathScoreSAT",
AttributeValue::N(student_info.math_sat_score.to_string()),
)
.expression_attribute_names("#studentFirstName", "studentFirstName") .expression_attribute_names("#studentFirstName", "studentFirstName")
.expression_attribute_names("#studentLastName", "studentLastName") .expression_attribute_names("#studentLastName", "studentLastName")
.expression_attribute_names("#mathScoreSAT", "mathScoreSAT")
.update_expression( .update_expression(
"SET #studentFirstName = :studentFirstName, #studentLastName = :studentLastName", concat!("SET #studentFirstName = :studentFirstName, ",
"#studentLastName = :studentLastName, ",
"#mathScoreSAT = :mathScoreSAT"),
) )
.send() .send()
.await .await
@@ -86,65 +110,98 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
#[component] #[component]
pub fn HomePage() -> impl IntoView { pub fn HomePage() -> impl IntoView {
let sample_id = RwSignal::new("google_113247439743075864879".to_string());
// Creates a reactive value to update the button // Creates a reactive value to update the button
let count = RwSignal::new(0);
let elements_disabled = RwSignal::new(false);
let auth = use_context::<AuthSignal>().expect("Couldn't find user information.");
let user_claims = Signal::derive(move || {
auth.with(|auth| {
auth.authenticated().and_then(|data| {
data.decoded_access_token::<UserClaims>(Algorithm::RS256, &["account"])
})
})
});
// Note that the value passed in MUST be equatable.
// We get/unwrap the value repeatedly until we get a simple string value, then clone it so that
// we don't lose access to it in the future, should we need it again.
let server_resource = Resource::new( let server_resource = Resource::new(
move || sample_id.get(), move || user_claims.get().map(|claim| claim.claims.subject.clone()),
async |id| { async |opt_username| match opt_username {
get_submission(id).await.unwrap_or_else(|e| { Some(username) => get_submission(username).await.unwrap_or_else(|e| {
console_log(e.to_string().as_str()); console_log(e.to_string().as_str());
StudentInfo { StudentInfo {
first_name: String::from("Error"), first_name: String::from("Error"),
last_name: String::from(""), last_name: String::from(""),
math_sat_score: 0,
} }
}) }),
None => StudentInfo {
first_name: String::new(),
last_name: String::new(),
math_sat_score: 0,
},
}, },
); );
let on_click = move |_| {
*count.write() += 1;
*elements_disabled.write() = count.get() == 10;
};
let submit_action = ServerAction::<CreateSampleSubmission>::new(); let submit_action = ServerAction::<CreateSampleSubmission>::new();
view! { view! {
<h1>"Welcome to Leptos!"</h1> <AuthLoaded fallback=Loading>
<p> <Authenticated unauthenticated=Unauthenticated>
"Lillian's reported full name from the API: " // Replace this fallback with a real loading screen.
<Suspense fallback=move || view! { <span>"Loading..."</span> }> <Suspense fallback=Loading>
{move || { {move || {
server_resource.get().map(|submission| { server_resource.get().map(|submission: StudentInfo| {
view! { <span>{submission.first_name}" "{submission.last_name}</span> } let reactive_info = StudentInfoReactive::new(submission);
})
}} let select_value = RwSignal::new(String::from("Math"));
</Suspense>
</p> view! {
<ActionForm action=submit_action> <p>
<div> "Current user's reported full name from the API: "
<OutlinedTextField {reactive_info.first_name}" "{reactive_info.last_name}
label="First Name".into() </p>
placeholder="John".into()
disabled={elements_disabled} <form>
name="student_info[first_name]".into() /> <div>
</div> <OutlinedTextField
<div> label="First Name".into()
<OutlinedTextField placeholder="John".into()
label="Last Name".into() value=reactive_info.first_name />
placeholder="Smith".into() </div>
disabled={elements_disabled} <div>
name="student_info[last_name]".into() /> <OutlinedTextField
</div> label="Last Name".into()
<div> placeholder="Smith".into()
<ActionButton value=reactive_info.last_name />
on:click=on_click </div>
disabled={elements_disabled} <div>
button_type="submit".into() <OutlinedTextField
> label="Testing".into()
"Submit" placeholder="Test".into()
</ActionButton> value=reactive_info.math_sat_score />
</div> </div>
</ActionForm> <Select
value_list = vec!["Math".into(), "English".into(), "Science".into()]
value = select_value
/>
<div>
<ActionButton
on:click=move |_| {
submit_action.dispatch(CreateSampleSubmission {
student_info: reactive_info.capture(),
subject: user_claims.get().unwrap().claims.subject.clone()
});
}
>
"Submit"
</ActionButton>
</div>
</form>
}
})
}}
</Suspense>
</Authenticated>
</AuthLoaded>
} }
} }
+5 -8
View File
@@ -1,23 +1,20 @@
//! This defines the base component of the Chromebook loaner kiosk application. //! This defines the base component of the Chromebook loaner kiosk application.
//! There are going to be 3 definitive screens that the users are able to see and use: //! There are going to be 3 definitive screens that the users are able to see and use:
//! - The username entry screen: users will enter their name and email. //! - The username entry screen: users will enter their name and email.
//! - The action selection screen: users will be given a choice regarding their loaner status. //! - The action selection screen: users will be given a choice regarding their loaner status.
//! - The completion screen: users will be given a success message should their information save. //! - The completion screen: users will be given a success message should their information save.
//! Refer to each component's file for more information about how they work. //! Refer to each component's file for more information about how they work.
use leptos::prelude::*; use super::{action_selection::ActionSelection, user_entry::UserEntry};
use crate::common::LoanerUser; use crate::common::LoanerUser;
use super::{ use leptos::prelude::*;
user_entry::UserEntry,
action_selection::ActionSelection
};
/// Used to keep track of the currently displayed controls. /// Used to keep track of the currently displayed controls.
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum LoanerKioskState { pub enum LoanerKioskState {
UserEntry, UserEntry,
ActionSelection, ActionSelection,
Complete Complete,
} }
/// The base Chromebook loaner kiosk component. /// The base Chromebook loaner kiosk component.
@@ -27,7 +24,7 @@ pub enum LoanerKioskState {
pub fn LoanerKiosk() -> impl IntoView { pub fn LoanerKiosk() -> impl IntoView {
let current_state = RwSignal::new(LoanerKioskState::UserEntry); let current_state = RwSignal::new(LoanerKioskState::UserEntry);
let user = RwSignal::new(LoanerUser::new()); let user = RwSignal::new(LoanerUser::new());
view! { view! {
<div class="flex w-screen h-screen justify-center items-center"> <div class="flex w-screen h-screen justify-center items-center">
<UserEntry <UserEntry
+2 -3
View File
@@ -1,4 +1,3 @@
mod loaner_kiosk;
mod user_entry;
mod action_selection; mod action_selection;
mod loaner_kiosk;
mod user_entry;
+12 -6
View File
@@ -1,4 +1,4 @@
//! Contains the user entry component. //! Contains the user entry component.
//! It would be preferred if the user was restricted to a real user account. This is the URL: //! It would be preferred if the user was restricted to a real user account. This is the URL:
//! https://admin.googleapis.com/admin/directory/v1/users?domain=region15.org&projection=BASIC&query=isArchived=false%20isAdmin=false //! https://admin.googleapis.com/admin/directory/v1/users?domain=region15.org&projection=BASIC&query=isArchived=false%20isAdmin=false
//! Once they select this, they can then check in/out the loaners under their account. //! Once they select this, they can then check in/out the loaners under their account.
@@ -6,10 +6,10 @@
//! a service account. That's going to be a pain to set up. //! a service account. That's going to be a pain to set up.
//! For example's sake here, we're just going to take a first name, last name, and email address. //! For example's sake here, we're just going to take a first name, last name, and email address.
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use crate::common::LoanerUser; use crate::common::LoanerUser;
use crate::components::{ActionButton, OutlinedTextField}; use crate::components::{ActionButton, OutlinedTextField};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
/// The user entry component for the loaner kiosk. /// The user entry component for the loaner kiosk.
/// ///
@@ -20,11 +20,14 @@ use crate::components::{ActionButton, OutlinedTextField};
#[component] #[component]
pub fn UserEntry( pub fn UserEntry(
/// A callback function used when the control has determined this stage is finished. /// A callback function used when the control has determined this stage is finished.
#[prop()] mut on_submit: impl FnMut() + 'static, #[prop()]
mut on_submit: impl FnMut() + 'static,
/// The global user record. /// The global user record.
#[prop()] user: RwSignal<LoanerUser>, #[prop()]
user: RwSignal<LoanerUser>,
/// Whether this control is shown. Required for use with CSS animations. /// Whether this control is shown. Required for use with CSS animations.
#[prop()] shown: bool #[prop()]
shown: bool,
) -> impl IntoView { ) -> impl IntoView {
let disabled = RwSignal::new(false); let disabled = RwSignal::new(false);
let on_click = move |_| { let on_click = move |_| {
@@ -44,16 +47,19 @@ pub fn UserEntry(
label="First Name".into() label="First Name".into()
placeholder="John".into() placeholder="John".into()
disabled={disabled} disabled={disabled}
value=RwSignal::new("".to_owned())
/> />
<OutlinedTextField <OutlinedTextField
label="Last Name".into() label="Last Name".into()
placeholder="Smith".into() placeholder="Smith".into()
disabled={disabled} disabled={disabled}
value=RwSignal::new("".to_owned())
/> />
<OutlinedTextField <OutlinedTextField
label="Region 15 Email".into() label="Region 15 Email".into()
placeholder="jsmith@region15.org".into() placeholder="jsmith@region15.org".into()
disabled={disabled} disabled={disabled}
value=RwSignal::new("".to_owned())
/> />
<ActionButton <ActionButton
on:click=on_click on:click=on_click
+1 -1
View File
@@ -1,6 +1,6 @@
mod about_page; mod about_page;
mod home_page; mod home_page;
mod provider_entry;
mod loaner_kiosk; mod loaner_kiosk;
mod provider_entry;
pub use self::{about_page::*, home_page::*, provider_entry::*}; pub use self::{about_page::*, home_page::*, provider_entry::*};
+38 -19
View File
@@ -1,27 +1,46 @@
use crate::components::{MultiEntry, MultiEntryData, MultiEntryMember, OutlinedTextField}; use crate::app::Unauthenticated;
use crate::common::UserClaims;
use crate::components::{Loading, MultiEntry, MultiEntryData, MultiEntryMember, OutlinedTextField};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*; use leptos::prelude::*;
use leptos_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
use serde::{Deserialize, Serialize};
#[component] #[component]
pub fn ProviderEntry() -> impl IntoView { pub fn ProviderEntry() -> impl IntoView {
let auth = use_context::<AuthSignal>().expect("Couldn't find auth signal");
let auth_data = auth
.get()
.authenticated()
.and_then(|data| data.decoded_access_token::<UserClaims>(Algorithm::RS256, &["account"]));
let multi_entries: RwSignal<Vec<MultiEntryData>> = RwSignal::new(vec![]); let multi_entries: RwSignal<Vec<MultiEntryData>> = RwSignal::new(vec![]);
console_log(format!("User claims: {auth_data:?}").as_str());
view! ( view! (
<h1>This is the provider entry page</h1> <AuthLoaded fallback=Loading>
<h2>It would usually have a series of questions that we ask the providers</h2> <Authenticated unauthenticated=Unauthenticated>
<div> <LogoutLink class="text-logout">"Sign out"</LogoutLink>
<OutlinedTextField <h1>This is the provider entry page</h1>
placeholder = "Testing information...".into() <h2>It would usually have a series of questions that we ask the providers</h2>
name = "testing_input".into() <div>
label = "This is a testing question.".into() <OutlinedTextField
/> placeholder = "Testing information...".into()
</div> name = "testing_input".into()
<div> label = "This is a testing question.".into()
<p>This is an example of the multientry element.</p> value = RwSignal::new("".to_owned())
<MultiEntry />
entries=multi_entries </div>
name_member=Signal::from(MultiEntryMember::from_str("Entry Name", "name")) <div>
info_member=Signal::from(MultiEntryMember::from_str("Entry Info", "info")) <p>This is an example of the multientry element.</p>
/> <MultiEntry
</div> entries=multi_entries
name_member=Signal::from(MultiEntryMember::from_str("Entry Name", "name"))
info_member=Signal::from(MultiEntryMember::from_str("Entry Info", "info"))
/>
</div>
</Authenticated>
</AuthLoaded>
) )
} }