feat: create file download capability
This commit is contained in:
1 parent
c5b11481b3
commit
f8d399f9ae
4 files changed
+321
-25
No files matched your search
Generated
+202
@@ -2,6 +2,23 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
@@ -739,6 +756,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
|
||||
dependencies = [
|
||||
"libbz2-rs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.2.2"
|
||||
@@ -789,6 +815,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.57"
|
||||
@@ -895,6 +931,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.6.0"
|
||||
@@ -1173,6 +1215,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate64"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.6.1"
|
||||
@@ -1359,6 +1407,16 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"miniz_oxide",
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -2237,6 +2295,15 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interpolator"
|
||||
version = "0.5.0"
|
||||
@@ -2683,6 +2750,12 @@ dependencies = [
|
||||
"tachys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
@@ -2725,6 +2798,15 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rust2"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
@@ -2795,6 +2877,16 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.1.1"
|
||||
@@ -2958,6 +3050,16 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.6"
|
||||
@@ -3016,6 +3118,12 @@ dependencies = [
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
@@ -3031,6 +3139,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppmd-rust"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -3621,6 +3735,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"console_error_panic_hook",
|
||||
"csv",
|
||||
"futures",
|
||||
"icondata",
|
||||
"indexmap",
|
||||
"jsonwebtoken 10.3.0",
|
||||
@@ -3640,6 +3755,7 @@ dependencies = [
|
||||
"traits",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3931,6 +4047,12 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.4"
|
||||
@@ -4177,6 +4299,7 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"js-sys",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
@@ -4464,6 +4587,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-path"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
@@ -5311,8 +5440,81 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "8.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcab981e19633ebcf0b001ddd37dd802996098bc1864f90b7c5d970ce76c1d59"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bzip2",
|
||||
"constant_time_eq",
|
||||
"crc32fast",
|
||||
"deflate64",
|
||||
"flate2",
|
||||
"getrandom 0.4.1",
|
||||
"hmac",
|
||||
"indexmap",
|
||||
"lzma-rust2",
|
||||
"memchr",
|
||||
"pbkdf2",
|
||||
"ppmd-rust",
|
||||
"sha1",
|
||||
"time",
|
||||
"typed-path",
|
||||
"zeroize",
|
||||
"zopfli",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"crc32fast",
|
||||
"log",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.16+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
@@ -34,6 +34,8 @@ csv = "1.4.0"
|
||||
leptos-use = { version = "0.18.3", features = ["use_drop_zone"] }
|
||||
jsonwebtoken = { version = "10.3.0", features = ["aws_lc_rs"], optional = true }
|
||||
reqwest = { version = "0.13.2", features = ["json"] }
|
||||
zip = { version = "8.5.1", optional = true }
|
||||
futures = { version = "0.3.32", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["ssr"]
|
||||
@@ -51,6 +53,8 @@ ssr = [
|
||||
"dep:aws-config",
|
||||
"dep:serde_dynamo",
|
||||
"dep:jsonwebtoken",
|
||||
"dep:zip",
|
||||
"dep:futures",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
|
||||
+51
-17
@@ -1,6 +1,7 @@
|
||||
use leptos::prelude::*;
|
||||
use leptos::server_fn::codec::Json;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
mod imports {
|
||||
@@ -12,6 +13,7 @@ mod imports {
|
||||
pub use aws_sdk_dynamodb::types::AttributeValue;
|
||||
pub use leptos::logging::{debug_log, error};
|
||||
pub use std::collections::HashMap;
|
||||
pub use zip::write::SimpleFileOptions;
|
||||
}
|
||||
|
||||
#[server(input = Json)]
|
||||
@@ -211,7 +213,7 @@ pub async fn get_completed_students(
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// # Get Student File API
|
||||
/// # Get File by Key API
|
||||
/// This function gets a file from the corresponding S3 file key. If the subject found from the
|
||||
/// `access_token` does not match the owner of the requested file, the requesting user must have
|
||||
/// provider-level access or higher.
|
||||
@@ -221,7 +223,7 @@ pub async fn get_completed_students(
|
||||
/// if the requesting user does not have the correct permissions, it will return a message indicating
|
||||
/// such. If the file is not found, an error containing "File not found" will be returned.
|
||||
#[server]
|
||||
pub async fn get_student_file(
|
||||
pub async fn get_file_by_key(
|
||||
access_token: String,
|
||||
file_key: String,
|
||||
) -> Result<Vec<u8>, ServerFnError> {
|
||||
@@ -262,29 +264,18 @@ pub async fn get_student_file(
|
||||
Ok(bytes.to_vec())
|
||||
}
|
||||
|
||||
/// # Batch Get Student Files API
|
||||
/// Gets all indicated files and returns a single `.zip` file containing all of them. They will be
|
||||
/// organized based on their file key.
|
||||
///
|
||||
/// This is more efficient than calling the [`get_student_file`] function as it makes use of S3's
|
||||
/// `batch_get_object` function.
|
||||
#[server]
|
||||
pub async fn batch_get_student_files() -> Result<Vec<u8>, ServerFnError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// # Get Student File Names API
|
||||
/// Gets a list of file names given a student's ID and the corresponding input ID from the forms.
|
||||
/// These file names can be used to construct a file key for S3.
|
||||
///
|
||||
/// This API is only accessible by provider-level users.
|
||||
#[server]
|
||||
pub async fn get_student_file_names(
|
||||
pub async fn get_student_files(
|
||||
access_token: String,
|
||||
student_id: String,
|
||||
form_name: String,
|
||||
question_id: String,
|
||||
) -> Result<Vec<String>, ServerFnError> {
|
||||
) -> Result<Vec<u8>, ServerFnError> {
|
||||
use imports::*;
|
||||
|
||||
let claims = validate_and_get_token_info(access_token).await?;
|
||||
@@ -296,7 +287,7 @@ pub async fn get_student_file_names(
|
||||
|
||||
let client = create_dynamo_client().await;
|
||||
|
||||
client
|
||||
let file_keys = client
|
||||
.query()
|
||||
.table_name(MAIN_TABLE_NAME)
|
||||
.expression_attribute_values(":hk", AttributeValue::S(format!("STUDENT#{student_id}")))
|
||||
@@ -319,9 +310,52 @@ pub async fn get_student_file_names(
|
||||
|
||||
items
|
||||
.into_iter()
|
||||
.filter_map(|item| item.get("SK")?.as_s().ok().cloned())
|
||||
.filter_map(|item| item.get("file_key")?.as_s().ok().cloned())
|
||||
.collect::<Vec<String>>()
|
||||
})?;
|
||||
|
||||
debug_log!("Await student file futures...");
|
||||
let futures: Vec<_> = file_keys
|
||||
.into_iter()
|
||||
.map(|file_key| async move {
|
||||
debug_log!("Requesting file from API with key {file_key}");
|
||||
// Get all files from S3. This is a batch operation.
|
||||
let s3_client = aws_sdk_s3::Client::new(&create_aws_config().await);
|
||||
let file_output = s3_client
|
||||
.get_object()
|
||||
.bucket("leptos-scholarships")
|
||||
.key(&file_key)
|
||||
.send()
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
let bytes = file_output.body.collect().await.ok()?.to_vec();
|
||||
|
||||
Some((file_key, bytes))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let results = futures::future::join_all(futures)
|
||||
.await
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<(String, Vec<u8>)>>();
|
||||
|
||||
let cur = std::io::Cursor::new(Vec::new());
|
||||
let mut writer = zip::ZipWriter::new(cur);
|
||||
|
||||
debug_log!("Writing {} files to zip file...", results.len());
|
||||
for (key, file_bytes) in results {
|
||||
writer
|
||||
.start_file(key.clone(), SimpleFileOptions::default())
|
||||
.map_err(ServerFnError::new)?;
|
||||
writer.write_all(&file_bytes).map_err(ServerFnError::new)?;
|
||||
}
|
||||
|
||||
debug_log!("Getting finished file");
|
||||
let finished = writer.finish().map_err(ServerFnError::new)?;
|
||||
|
||||
Ok(finished.into_inner())
|
||||
}
|
||||
|
||||
/// # Get All Input Files API
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
use crate::components::{ActionButton, Banner, Loading, ValueDisplay};
|
||||
use crate::pages::UnauthenticatedPage;
|
||||
use crate::pages::api::get_comparison_info;
|
||||
use crate::pages::api::students::{
|
||||
BatchGetStudentFiles, GetStudentFile, GetStudentFileNames, get_all_input_files,
|
||||
get_all_student_data, get_student_data, get_student_file_names,
|
||||
};
|
||||
use crate::pages::api::students::{GetStudentFiles, get_all_input_files, get_student_data};
|
||||
use base64::Engine;
|
||||
use leptos::ev::{Event, MouseEvent};
|
||||
use leptos::html::Dialog;
|
||||
use leptos::logging::debug_log;
|
||||
use leptos::prelude::*;
|
||||
use leptos::wasm_bindgen::JsCast;
|
||||
use leptos::web_sys::HtmlAnchorElement;
|
||||
use leptos_oidc::{AuthLoaded, AuthSignal, Authenticated};
|
||||
use leptos_router::components::Outlet;
|
||||
use leptos_router::hooks::{query_signal, use_navigate};
|
||||
@@ -501,9 +501,12 @@ fn ApplicantsStudentListView(
|
||||
// flex container.
|
||||
view! {
|
||||
<StudentInformationDialog
|
||||
scholarship_id=scholarship.get().subject
|
||||
student_id=current_student_id
|
||||
visible=dialog_visible
|
||||
on_close=on_dialog_close
|
||||
essay_required=essay_required
|
||||
fafsa_required=fafsa_required
|
||||
/>
|
||||
<Show when=move || !error_msg.get().is_empty()>
|
||||
<div>"Error checking student eligibility: "{error_msg}</div>
|
||||
@@ -550,9 +553,11 @@ fn ApplicantsStudentListView(
|
||||
|
||||
#[component]
|
||||
fn StudentInformationDialog(
|
||||
#[prop(into)] scholarship_id: Signal<String>,
|
||||
#[prop(into)] student_id: Signal<Option<String>>,
|
||||
// #[prop(into)] scholarship_info: StoredValue<ExpandableInfo>,
|
||||
#[prop(into)] visible: Signal<bool>,
|
||||
#[prop(into)] fafsa_required: Signal<bool>,
|
||||
#[prop(into)] essay_required: Signal<bool>,
|
||||
#[prop(into)] on_close: Callback<()>,
|
||||
) -> impl IntoView {
|
||||
// This component needs to just display all student information.
|
||||
@@ -560,6 +565,9 @@ fn StudentInformationDialog(
|
||||
// requested an essay or financial information.
|
||||
|
||||
let dialog_ref = NodeRef::<Dialog>::new();
|
||||
let auth = expect_context::<AuthSignal>();
|
||||
let access_token =
|
||||
Memo::new(move |_| auth.with(|auth| auth.authenticated().map(|a| a.access_token())));
|
||||
|
||||
//#region Schema Definitions
|
||||
|
||||
@@ -635,13 +643,50 @@ fn StudentInformationDialog(
|
||||
// We want to get the student's uploaded files - only the ones that are relevant to the current
|
||||
// scholarship, meaning their essays and/or FAFSA by request.
|
||||
|
||||
let get_student_files = ServerAction::<GetStudentFiles>::new();
|
||||
let get_buttons_disabled = get_student_files.pending();
|
||||
let on_click_fafsa = move |_| {
|
||||
// We need to get the student's file names/keys and then tell the server that we want to
|
||||
// download them.
|
||||
let fafsa_key = format!("financial_info/user_id/fafsa/file_name");
|
||||
debug_log!("Getting FAFSA file with key {fafsa_key}");
|
||||
get_student_files.dispatch(GetStudentFiles {
|
||||
access_token: access_token.get().unwrap_or_default(),
|
||||
student_id: student_id.get().unwrap_or_default(),
|
||||
form_name: "financial_info".to_string(),
|
||||
question_id: "fafsa".to_string(),
|
||||
});
|
||||
};
|
||||
|
||||
let on_click_essay = move |_| {
|
||||
debug_log!("Getting essay files");
|
||||
get_student_files.dispatch(GetStudentFiles {
|
||||
access_token: access_token.get().unwrap_or_default(),
|
||||
student_id: student_id.get().unwrap_or_default(),
|
||||
form_name: "scholarship_essays".to_string(),
|
||||
question_id: scholarship_id.get(),
|
||||
});
|
||||
};
|
||||
|
||||
Effect::new(move || {
|
||||
if let Some(Ok(file)) = get_student_files.value().get() {
|
||||
let base64 = base64::engine::general_purpose::STANDARD.encode(file);
|
||||
let data_url = format!("data:application/zip;base64,{}", base64);
|
||||
// Create a base64 link and open it.
|
||||
let a: HtmlAnchorElement = document()
|
||||
.create_element("a")
|
||||
.expect("Failed to create anchor element")
|
||||
.dyn_into()
|
||||
.unwrap();
|
||||
|
||||
a.set_download("testing.zip");
|
||||
a.set_href(&data_url);
|
||||
document().body().unwrap().append_child(&a).unwrap();
|
||||
a.click();
|
||||
a.remove();
|
||||
|
||||
get_student_files.clear();
|
||||
}
|
||||
});
|
||||
|
||||
view! {
|
||||
<dialog
|
||||
node_ref=dialog_ref
|
||||
@@ -653,7 +698,18 @@ fn StudentInformationDialog(
|
||||
>
|
||||
<div class="p-5" on:click=move |e: MouseEvent| e.stop_propagation()>
|
||||
<Suspense fallback=Loading>
|
||||
<ActionButton on:click=on_click_fafsa>"Get FAFSA"</ActionButton>
|
||||
<div class="flex flex-row">
|
||||
<Show when=move || fafsa_required.get()>
|
||||
<ActionButton on:click=on_click_fafsa disabled=get_buttons_disabled>
|
||||
"Get FAFSA File(s)"
|
||||
</ActionButton>
|
||||
</Show>
|
||||
<Show when=move || essay_required.get()>
|
||||
<ActionButton on:click=on_click_essay disabled=get_buttons_disabled>
|
||||
"Get Essay File(s)"
|
||||
</ActionButton>
|
||||
</Show>
|
||||
</div>
|
||||
{move || Suspend::new(async move {
|
||||
get_student_data(
|
||||
student_id.get().unwrap_or_default(),
|
||||
|
||||
Reference in new issue
Block a user