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",
]
[[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]]
name = "any_spawner"
version = "0.3.0"
@@ -565,7 +580,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -688,6 +703,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "clang-sys"
version = "1.8.1"
@@ -858,6 +888,41 @@ dependencies = [
"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]]
name = "dashmap"
version = "6.1.0"
@@ -878,6 +943,18 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "deranged"
version = "0.4.0"
@@ -1134,8 +1211,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@@ -1482,6 +1561,30 @@ dependencies = [
"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]]
name = "icu_collections"
version = "1.5.0"
@@ -1600,6 +1703,12 @@ dependencies = [
"syn",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
@@ -1657,6 +1766,12 @@ dependencies = [
"libc",
]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "itertools"
version = "0.10.5"
@@ -1701,6 +1816,19 @@ dependencies = [
"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]]
name = "lazy_static"
version = "1.5.0"
@@ -1734,7 +1862,7 @@ dependencies = [
"oco_ref",
"or_poisoned",
"paste",
"rand",
"rand 0.9.2",
"reactive_graph",
"rustc-hash 2.1.1",
"rustc_version",
@@ -1755,6 +1883,26 @@ dependencies = [
"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]]
name = "leptos_axum"
version = "0.8.5"
@@ -1878,6 +2026,26 @@ dependencies = [
"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]]
name = "leptos_router"
version = "0.8.5"
@@ -1948,7 +2116,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
"windows-targets",
"windows-targets 0.53.3",
]
[[package]]
@@ -1985,6 +2153,14 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "macros"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "manyhow"
version = "0.11.4"
@@ -2129,6 +2305,25 @@ dependencies = [
"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]]
name = "object"
version = "0.36.7"
@@ -2198,7 +2393,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -2368,14 +2563,35 @@ version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
"rand_chacha 0.9.0",
"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]]
@@ -2385,7 +2601,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"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]]
@@ -2493,6 +2718,42 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "ring"
version = "0.17.14"
@@ -2693,9 +2954,12 @@ dependencies = [
"leptos",
"leptos_axum",
"leptos_meta",
"leptos_oidc",
"leptos_router",
"macros",
"serde",
"tokio",
"url",
"uuid",
"wasm-bindgen",
]
@@ -2999,6 +3263,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
@@ -3033,6 +3303,9 @@ name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
dependencies = [
"futures-core",
]
[[package]]
name = "synstructure"
@@ -3377,7 +3650,7 @@ dependencies = [
"http 1.3.1",
"httparse",
"log",
"rand",
"rand 0.9.2",
"sha1",
"thiserror 2.0.12",
"utf-8",
@@ -3448,6 +3721,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
@@ -3678,13 +3952,92 @@ dependencies = [
"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]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -3693,7 +4046,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@@ -3702,14 +4055,31 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"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]]
@@ -3718,48 +4088,96 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "winnow"
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 }
serde = "1.0.219"
uuid = { version = "1.17.0", features = ["serde", "v4", "js"] }
leptos_oidc = "0.9.0"
url = "2.5.4"
macros = { path = "./macros" }
[features]
hydrate = [
+16 -86
View File
@@ -1,91 +1,21 @@
<picture>
<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>
# Region 15 Scholarship Application (Rust Rewrite)
# 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
cargo install cargo-leptos --locked
```
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.
- Build the application's custom elements
- Build out the database requests
- Create proper authentication
- PDF generation using data (server-side)
+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_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
use leptos_oidc::{
Auth, AuthLoaded, AuthParameters, AuthSignal, Authenticated, Challenge, LoginLink, LogoutLink,
};
use leptos_router::{
components::{Route, Router, Routes},
StaticSegment,
};
use url::Url;
use crate::pages::{
AboutPage,
HomePage,
ProviderEntry
};
use crate::pages::{AboutPage, HomePage, ProviderEntry};
pub fn shell(options: LeptosOptions) -> impl IntoView {
view! {
@@ -34,17 +34,38 @@ pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
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! {
// injects a stylesheet into the document <head>
// id=leptos means cargo-leptos will hot-reload this stylesheet
<Stylesheet id="leptos" href="/pkg/scholarships-rs-wasm.css"/>
// <AuthLoading><p>"Authentication is loading"</p></AuthLoading>
// <AuthErrorContext><AuthErrorView/></AuthErrorContext>
// sets the document title
<Title text="Welcome to Leptos"/>
// content for this welcome page
<Router>
<main>
// TODO Create a 404 page
<Routes fallback=|| "Page not found.".into_view()>
<Route path=StaticSegment("") view=HomePage/>
<Route path=StaticSegment("about") view=AboutPage/>
@@ -54,3 +75,23 @@ pub fn App() -> impl IntoView {
</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 serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct FormDefinition {
sections: Vec<Section>
sections: Vec<Section>,
}
#[derive(Serialize, Deserialize)]
struct Section {
questions: Vec<QuestionType>
questions: Vec<QuestionType>,
}
#[derive(Serialize, Deserialize)]
@@ -20,12 +19,10 @@ enum QuestionType {
Dropdown,
Checkbox,
Radio,
Multi
Multi,
}
struct TextInput {
}
struct TextInput {}
#[derive(Serialize, Deserialize)]
struct QuestionComponent {
@@ -34,5 +31,5 @@ struct QuestionComponent {
placeholder: Option<String>,
// 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)]
pub struct LoanerUser {
pub first_name: String,
pub last_name: String,
pub email: String,
pub has_loan: bool
pub has_loan: bool,
}
impl LoanerUser {
@@ -13,7 +12,7 @@ impl LoanerUser {
first_name: "".to_string(),
last_name: "".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 loaner_user;
mod student_info;
mod user;
pub use self::{
loaner_user::*,
student_info::*
};
pub use self::{loaner_user::*, student_info::*, user::*};
+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 first_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 text_field;
mod loading;
mod multi_entry;
mod text_field;
mod select;
mod checkboxes;
pub use self::{
action_button::*,
text_field::*,
multi_entry::*
};
pub use self::{action_button::*, loading::*, multi_entry::*, text_field::*, select::*};
+14 -15
View File
@@ -1,9 +1,9 @@
use std::collections::HashMap;
use crate::components::ActionButton;
use leptos::{leptos_dom::logging::console_log, prelude::*};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::components::ActionButton;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct MultiEntryData {
@@ -27,7 +27,7 @@ pub struct MultiEntryMember {
/// The name of the member, used for display only.
pub display_name: String,
/// 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 {
@@ -35,7 +35,7 @@ impl MultiEntryMember {
pub fn new() -> Self {
Self {
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 {
Self {
display_name,
member_name
member_name,
}
}
@@ -51,7 +51,7 @@ impl MultiEntryMember {
pub fn from_str(display_name: &str, member_name: &str) -> Self {
Self {
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.
#[prop()]
info_member: Signal<MultiEntryMember>,
/// The child components of this Entry view.
#[prop()]
children: Children
) -> impl IntoView {
let nameData = entry_data.data.get(&name_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>,
/// The secondary display member for each entry object.
#[prop()]
info_member: Signal<MultiEntryMember>
info_member: Signal<MultiEntryMember>,
) -> impl IntoView {
// Creates an entry in the given entries list.
let create_entry = move |_| {
let mut new_entry = MultiEntryData::new();
new_entry
.data
.insert(name_member.get().member_name, ValueType::String(String::from("Testing")));
new_entry
.data
.insert(info_member.get().member_name, ValueType::String(new_entry.id.to_string()));
new_entry.data.insert(
name_member.get().member_name,
ValueType::String(String::from("Testing")),
);
new_entry.data.insert(
info_member.get().member_name,
ValueType::String(new_entry.id.to_string()),
);
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::*;
#[component]
pub fn OutlinedTextField(
pub fn OutlinedTextField<T>(
#[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 = String::from(""))] name: String,
#[prop(default = String::from(""))] label: 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! (
<label>
{ label }
<input
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-gray-600", "pointer-events-none"], move || disabled.get())
class=(["border-red-700", "bg-transparent"], move || !disabled.get())
class=(["border-gray-600", "pointer-events-none", "bg-gray-600/33"], move || disabled.get())
r#type={input_type}
disabled={disabled}
placeholder={placeholder}
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>
)
+3 -1
View File
@@ -1,7 +1,9 @@
use leptos::prelude::event_target_value;
pub mod app;
mod common;
mod components;
mod pages;
mod common;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
+125 -68
View File
@@ -1,38 +1,54 @@
use crate::components::{ActionButton, OutlinedTextField};
use crate::common::StudentInfo;
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use std::collections::HashMap;
#[cfg(feature = "ssr")]
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")]
pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
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
.get_item()
.table_name("student-applications")
.key("Email", AttributeValue::S(id))
.key("Email", AttributeValue::S(id.clone()))
.send()
.await
{
Ok(output) => {
console_log("Found submission with passed key.");
let item = output.item.unwrap();
let first_name = item
let first_name: String = item
.get("studentFirstName")
.unwrap()
.as_s()
.unwrap()
.clone();
let last_name = item.get("studentLastName").unwrap().as_s().unwrap().clone();
.and_then(|attr| attr.as_s().ok())
.map(|s| s.to_owned())
.unwrap_or_default();
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 {
first_name,
last_name,
math_sat_score,
})
}
Err(err) => {
@@ -43,7 +59,10 @@ pub async fn get_submission(id: String) -> Result<StudentInfo, ServerFnError> {
}
#[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};
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(
format!(
"Creating sample submission with name {} {}",
student_info.first_name, student_info.last_name
"Creating sample submission with name {} {}, math score {}, and subject {}",
student_info.first_name, student_info.last_name, student_info.math_sat_score, subject
)
.as_str(),
);
let submission_id = String::from("testing_student");
match dbclient
.update_item()
.table_name("student-applications")
.key("Email", AttributeValue::S(submission_id))
.key("Email", AttributeValue::S(subject))
.expression_attribute_values(
":studentFirstName",
AttributeValue::S(student_info.first_name),
@@ -71,10 +88,17 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
":studentLastName",
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("#studentLastName", "studentLastName")
.expression_attribute_names("#mathScoreSAT", "mathScoreSAT")
.update_expression(
"SET #studentFirstName = :studentFirstName, #studentLastName = :studentLastName",
concat!("SET #studentFirstName = :studentFirstName, ",
"#studentLastName = :studentLastName, ",
"#mathScoreSAT = :mathScoreSAT"),
)
.send()
.await
@@ -86,65 +110,98 @@ pub async fn create_sample_submission(student_info: StudentInfo) -> Result<(), S
#[component]
pub fn HomePage() -> impl IntoView {
let sample_id = RwSignal::new("google_113247439743075864879".to_string());
// 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(
move || sample_id.get(),
async |id| {
get_submission(id).await.unwrap_or_else(|e| {
move || user_claims.get().map(|claim| claim.claims.subject.clone()),
async |opt_username| match opt_username {
Some(username) => get_submission(username).await.unwrap_or_else(|e| {
console_log(e.to_string().as_str());
StudentInfo {
first_name: String::from("Error"),
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();
view! {
<h1>"Welcome to Leptos!"</h1>
<p>
"Lillian's reported full name from the API: "
<Suspense fallback=move || view! { <span>"Loading..."</span> }>
{move || {
server_resource.get().map(|submission| {
view! { <span>{submission.first_name}" "{submission.last_name}</span> }
})
}}
</Suspense>
</p>
<ActionForm action=submit_action>
<div>
<OutlinedTextField
label="First Name".into()
placeholder="John".into()
disabled={elements_disabled}
name="student_info[first_name]".into() />
</div>
<div>
<OutlinedTextField
label="Last Name".into()
placeholder="Smith".into()
disabled={elements_disabled}
name="student_info[last_name]".into() />
</div>
<div>
<ActionButton
on:click=on_click
disabled={elements_disabled}
button_type="submit".into()
>
"Submit"
</ActionButton>
</div>
</ActionForm>
<AuthLoaded fallback=Loading>
<Authenticated unauthenticated=Unauthenticated>
// Replace this fallback with a real loading screen.
<Suspense fallback=Loading>
{move || {
server_resource.get().map(|submission: StudentInfo| {
let reactive_info = StudentInfoReactive::new(submission);
let select_value = RwSignal::new(String::from("Math"));
view! {
<p>
"Current user's reported full name from the API: "
{reactive_info.first_name}" "{reactive_info.last_name}
</p>
<form>
<div>
<OutlinedTextField
label="First Name".into()
placeholder="John".into()
value=reactive_info.first_name />
</div>
<div>
<OutlinedTextField
label="Last Name".into()
placeholder="Smith".into()
value=reactive_info.last_name />
</div>
<div>
<OutlinedTextField
label="Testing".into()
placeholder="Test".into()
value=reactive_info.math_sat_score />
</div>
<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:
//! - 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 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.
use leptos::prelude::*;
use super::{action_selection::ActionSelection, user_entry::UserEntry};
use crate::common::LoanerUser;
use super::{
user_entry::UserEntry,
action_selection::ActionSelection
};
use leptos::prelude::*;
/// Used to keep track of the currently displayed controls.
#[derive(Clone, Copy, PartialEq)]
pub enum LoanerKioskState {
UserEntry,
ActionSelection,
Complete
Complete,
}
/// The base Chromebook loaner kiosk component.
@@ -27,7 +24,7 @@ pub enum LoanerKioskState {
pub fn LoanerKiosk() -> impl IntoView {
let current_state = RwSignal::new(LoanerKioskState::UserEntry);
let user = RwSignal::new(LoanerUser::new());
view! {
<div class="flex w-screen h-screen justify-center items-center">
<UserEntry
+2 -3
View File
@@ -1,4 +1,3 @@
mod loaner_kiosk;
mod user_entry;
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:
//! 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.
@@ -6,10 +6,10 @@
//! 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.
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use crate::common::LoanerUser;
use crate::components::{ActionButton, OutlinedTextField};
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
/// The user entry component for the loaner kiosk.
///
@@ -20,11 +20,14 @@ use crate::components::{ActionButton, OutlinedTextField};
#[component]
pub fn UserEntry(
/// 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.
#[prop()] user: RwSignal<LoanerUser>,
#[prop()]
user: RwSignal<LoanerUser>,
/// Whether this control is shown. Required for use with CSS animations.
#[prop()] shown: bool
#[prop()]
shown: bool,
) -> impl IntoView {
let disabled = RwSignal::new(false);
let on_click = move |_| {
@@ -44,16 +47,19 @@ pub fn UserEntry(
label="First Name".into()
placeholder="John".into()
disabled={disabled}
value=RwSignal::new("".to_owned())
/>
<OutlinedTextField
label="Last Name".into()
placeholder="Smith".into()
disabled={disabled}
value=RwSignal::new("".to_owned())
/>
<OutlinedTextField
label="Region 15 Email".into()
placeholder="jsmith@region15.org".into()
disabled={disabled}
value=RwSignal::new("".to_owned())
/>
<ActionButton
on:click=on_click
+1 -1
View File
@@ -1,6 +1,6 @@
mod about_page;
mod home_page;
mod provider_entry;
mod loaner_kiosk;
mod 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_oidc::{Algorithm, AuthLoaded, AuthSignal, Authenticated, LogoutLink};
use serde::{Deserialize, Serialize};
#[component]
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![]);
console_log(format!("User claims: {auth_data:?}").as_str());
view! (
<h1>This is the provider entry page</h1>
<h2>It would usually have a series of questions that we ask the providers</h2>
<div>
<OutlinedTextField
placeholder = "Testing information...".into()
name = "testing_input".into()
label = "This is a testing question.".into()
/>
</div>
<div>
<p>This is an example of the multientry element.</p>
<MultiEntry
entries=multi_entries
name_member=Signal::from(MultiEntryMember::from_str("Entry Name", "name"))
info_member=Signal::from(MultiEntryMember::from_str("Entry Info", "info"))
/>
</div>
<AuthLoaded fallback=Loading>
<Authenticated unauthenticated=Unauthenticated>
<LogoutLink class="text-logout">"Sign out"</LogoutLink>
<h1>This is the provider entry page</h1>
<h2>It would usually have a series of questions that we ask the providers</h2>
<div>
<OutlinedTextField
placeholder = "Testing information...".into()
name = "testing_input".into()
label = "This is a testing question.".into()
value = RwSignal::new("".to_owned())
/>
</div>
<div>
<p>This is an example of the multientry element.</p>
<MultiEntry
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>
)
}