PR with too many features #58
No files matched your search
+25
-27
@@ -39,36 +39,34 @@ pub fn Banner(
|
|||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="mx-auto">
|
<div class="w-screen bg-red-900 shadow-lg shadow-[inset_0_2px_4px_0_rgba(0,0,0,0.3),0_4px_6px_-1px_rgba(127,29,29,0.3),0_2px_4px_-1px_rgba(127,29,29,0.2)]">
|
||||||
<div class="bg-red-900 shadow-lg shadow-[inset_0_2px_4px_0_rgba(0,0,0,0.3),0_4px_6px_-1px_rgba(127,29,29,0.3),0_2px_4px_-1px_rgba(127,29,29,0.2)]">
|
<div class="flex flex-row items-center gap-4 p-6 pb-2">
|
||||||
<div class="flex flex-row items-center gap-4 p-6 pb-2">
|
<div class="flex-shrink-0">
|
||||||
<div class="flex-shrink-0">
|
<img
|
||||||
<img
|
src=logo
|
||||||
src=logo
|
class="h-10 w-10 cursor-pointer"
|
||||||
class="h-10 w-10 cursor-pointer"
|
alt="icon"
|
||||||
alt="icon"
|
on:click=on_click
|
||||||
on:click=on_click
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
<h2 class="text-white text-2xl font-bold">{title.clone()}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="flex-1">
|
||||||
class="flex flex-row items-center gap-4"
|
<h2 class="text-white text-2xl font-bold">{title.clone()}</h2>
|
||||||
class=("p-2", move || !show_back.get())
|
|
||||||
>
|
|
||||||
<Show when=move || show_back.get()>
|
|
||||||
<a
|
|
||||||
href=path
|
|
||||||
class="flex flex-row items-center gap-2 m-2 ml-14 text-gray-200 hover:underline"
|
|
||||||
>
|
|
||||||
<Icon icon=icondata::FaArrowLeftSolid />
|
|
||||||
<span>"Back to Dashboard"</span>
|
|
||||||
</a>
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex flex-row items-center gap-4"
|
||||||
|
class=("p-2", move || !show_back.get())
|
||||||
|
>
|
||||||
|
<Show when=move || show_back.get()>
|
||||||
|
<a
|
||||||
|
href=path
|
||||||
|
class="flex flex-row items-center gap-2 m-2 ml-14 text-gray-200 hover:underline"
|
||||||
|
>
|
||||||
|
<Icon icon=icondata::FaArrowLeftSolid />
|
||||||
|
<span>"Back to Dashboard"</span>
|
||||||
|
</a>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ use crate::common::{InputType, ValueType};
|
|||||||
use crate::components::ActionButton;
|
use crate::components::ActionButton;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use leptos::logging::log;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
// region Helper Functions
|
// region Helper Functions
|
||||||
@@ -69,7 +70,7 @@ pub fn MultiEntry(
|
|||||||
#[prop(optional, into)] description: Signal<String>,
|
#[prop(optional, into)] description: Signal<String>,
|
||||||
/// The data member that contains the list of entries within the `data_map`.
|
/// The data member that contains the list of entries within the `data_map`.
|
||||||
#[prop(into)]
|
#[prop(into)]
|
||||||
data_member: String,
|
data_member: Signal<String>,
|
||||||
/// The data map that contains all the information. Data is found within this map using the
|
/// The data map that contains all the information. Data is found within this map using the
|
||||||
/// component's `data_member` input.
|
/// component's `data_member` input.
|
||||||
#[prop()]
|
#[prop()]
|
||||||
@@ -80,26 +81,31 @@ pub fn MultiEntry(
|
|||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
// Make the values within the list reactive. Each of these values should be a ValueType::Map,
|
// Make the values within the list reactive. Each of these values should be a ValueType::Map,
|
||||||
// so we can pass these directly to the application.
|
// so we can pass these directly to the application.
|
||||||
let data_reactive = RwSignal::new(
|
let data_list = Memo::new(move |_| {
|
||||||
data_map
|
data_map.with(|map| {
|
||||||
.get_untracked()
|
map.get(&data_member.get())
|
||||||
.get(&data_member)
|
.and_then(|v| v.as_list().ok().flatten())
|
||||||
.unwrap_or(&ValueType::List(None))
|
.unwrap_or_default()
|
||||||
.as_list().ok().flatten()
|
})
|
||||||
.unwrap_or(Vec::new())
|
|
||||||
);
|
|
||||||
|
|
||||||
// This effect updates the data_map with any new values from the data_reactive list, every time
|
|
||||||
// the data_reactive list is changed. This allows changes from within the Entry component to
|
|
||||||
// bubble up to the data_map effectively.
|
|
||||||
Effect::new(move |_| {
|
|
||||||
let entry_list = data_reactive.get();
|
|
||||||
data_map.update(|map| {
|
|
||||||
map.insert(data_member.clone(), ValueType::List(Some(entry_list)));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This effect updates the data_map with any new values from the data_list, every time
|
||||||
|
// the data_list is changed. This allows changes from within the Entry component to
|
||||||
|
// bubble up to the data_map effectively.
|
||||||
|
let update_parent_list = move |list| {
|
||||||
|
log!("Updating parent list using child list {:?}", list);
|
||||||
|
data_map.update(|map| {
|
||||||
|
map.insert(data_member.get(), ValueType::List(Some(list)));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
let add_entry = move |_| data_reactive.update(|list| list.push(new_entry()));
|
let add_entry = move |_| data_map.update(|map| {
|
||||||
|
log!("Adding new entry.");
|
||||||
|
let mut list = data_list.get_untracked();
|
||||||
|
|
||||||
|
list.push(new_entry());
|
||||||
|
map.insert(data_member.get(), ValueType::List(Some(list)));
|
||||||
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="flex flex-col flex-1">
|
<div class="flex flex-col flex-1">
|
||||||
@@ -112,33 +118,26 @@ pub fn MultiEntry(
|
|||||||
<div class="flex flex-col gap-2 p-2">
|
<div class="flex flex-col gap-2 p-2">
|
||||||
// Render the entries
|
// Render the entries
|
||||||
<Show
|
<Show
|
||||||
when=move || !data_reactive.get().is_empty()
|
when=move || !data_list.get().is_empty()
|
||||||
fallback=|| {
|
fallback=|| {
|
||||||
view! { <div class="mx-auto">"You haven't added any entries yet."</div> }
|
view! { <div class="mx-auto">"You haven't added any entries yet."</div> }
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<For
|
<For
|
||||||
each=move || data_reactive.get()
|
each=move || data_list.get()
|
||||||
// Entries MUST be given a unique ID. This is absolutely ridiculous.
|
// Entries MUST be given a unique ID. This is absolutely ridiculous.
|
||||||
key=|entry| get_uuid_from_map(&entry.as_map().unwrap().unwrap())
|
key=|entry| get_uuid_from_map(&entry.as_map().unwrap().unwrap())
|
||||||
children=move |mut entry_map| {
|
children=move |mut entry_map| {
|
||||||
let map_signal = RwSignal::new(
|
let map_signal = RwSignal::new(
|
||||||
entry_map.as_map().unwrap_or_default().unwrap_or_default(),
|
entry_map.as_map().ok().flatten().unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Effect::new(move |_| {
|
Effect::new(move |_| {
|
||||||
let child_map = map_signal.get();
|
let child_map = map_signal.get();
|
||||||
data_reactive
|
let mut list = data_list.get_untracked();
|
||||||
.update(|mut list| {
|
update_entry_list(&mut list, &child_map);
|
||||||
update_entry_list(&mut list, &child_map);
|
update_parent_list(list);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
// The map_signal is the map used for the internal element. This signal will
|
|
||||||
// keep track of the individual entry's data, and whenever it is changed,
|
|
||||||
// the parent data_map will also be updated to reflect these changes.
|
|
||||||
|
|
||||||
// This effect updates the data_reactive list with the map_signal values, every
|
|
||||||
// time the map_signal is changed.
|
|
||||||
// HashMap
|
|
||||||
|
|
||||||
view! { <Entry data_map=map_signal schema=schema /> }
|
view! { <Entry data_map=map_signal schema=schema /> }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ pub fn TabSidebarList(
|
|||||||
// Create a series of tabs based on a given list of names and paths.
|
// Create a series of tabs based on a given list of names and paths.
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="flex flex-row h-full w-full fixed">
|
<div class="flex flex-1 flex-row w-full">
|
||||||
<nav class="flex flex-col bg-red-900 w-64 h-full overflow-scroll">
|
<nav class="flex flex-col bg-red-900 w-64 overflow-scroll">
|
||||||
<For
|
<For
|
||||||
each=move || tabs.get()
|
each=move || tabs.get()
|
||||||
key=|info| info.text.clone()
|
key=|info| info.text.clone()
|
||||||
@@ -65,7 +65,7 @@ pub fn TabSidebarList(
|
|||||||
</For>
|
</For>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="flex flex-1 overflow-y-scroll">
|
<div class="flex flex-1 overflow-scroll">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+34
-26
@@ -10,32 +10,40 @@ pub fn StudentShell() -> impl IntoView {
|
|||||||
// Create login context
|
// Create login context
|
||||||
view! {
|
view! {
|
||||||
<StudentLoginContext>
|
<StudentLoginContext>
|
||||||
<Banner title="R15 Student Scholarship Application" logo="/PHS_Stacked_Acronym.png" />
|
<div class="h-screen flex flex-col overflow-hidden">
|
||||||
<AuthLoaded fallback=Loading>
|
<Banner
|
||||||
<Authenticated unauthenticated=UnauthenticatedPage>
|
title="R15 Student Scholarship Application"
|
||||||
<TabSidebarList
|
logo="/PHS_Stacked_Acronym.png"
|
||||||
base_path="students"
|
/>
|
||||||
tabs=vec![
|
|
||||||
TabInfo::new("Home", "home", None),
|
<div class="flex flex-1 min-h-0">
|
||||||
TabInfo::new("Demographics", "demographics", None),
|
<AuthLoaded fallback=Loading>
|
||||||
TabInfo::new(
|
<Authenticated unauthenticated=UnauthenticatedPage>
|
||||||
"Additional Info",
|
<TabSidebarList
|
||||||
"additional",
|
base_path="students"
|
||||||
Some(
|
tabs=vec![
|
||||||
vec![
|
TabInfo::new("Home", "home", None),
|
||||||
SubTabInfo::new("Academics", "academics"),
|
TabInfo::new("Demographics", "demographics", None),
|
||||||
SubTabInfo::new("Athletics", "athletics"),
|
TabInfo::new(
|
||||||
SubTabInfo::new("Work Experience", "work-experience"),
|
"Additional Info",
|
||||||
SubTabInfo::new("Extracurriculars", "extracurriculars"),
|
"additional",
|
||||||
SubTabInfo::new("University Info", "university"),
|
Some(
|
||||||
SubTabInfo::new("Family Info", "family-info"),
|
vec![
|
||||||
],
|
SubTabInfo::new("Academics", "academics"),
|
||||||
),
|
SubTabInfo::new("Athletics", "athletics"),
|
||||||
),
|
SubTabInfo::new("Work Experience", "work-experience"),
|
||||||
]
|
SubTabInfo::new("Extracurriculars", "extracurriculars"),
|
||||||
/>
|
SubTabInfo::new("University Info", "university"),
|
||||||
</Authenticated>
|
SubTabInfo::new("Family Info", "family-info"),
|
||||||
</AuthLoaded>
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
/>
|
||||||
|
</Authenticated>
|
||||||
|
</AuthLoaded>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</StudentLoginContext>
|
</StudentLoginContext>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user