From 535b7f19f7b1ce819df56d7a69c6e8f0a3e81230 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 12:49:12 +0000 Subject: [PATCH 01/22] Change control numbers to text --- thoth-api/migrations/0.1.0/up.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thoth-api/migrations/0.1.0/up.sql b/thoth-api/migrations/0.1.0/up.sql index b12219c5c..b9e568831 100644 --- a/thoth-api/migrations/0.1.0/up.sql +++ b/thoth-api/migrations/0.1.0/up.sql @@ -71,8 +71,8 @@ CREATE TABLE work ( license TEXT CHECK (license ~* '^[^:]*:\/\/(?:[^\/:]*:[^\/@]*@)?(?:[^\/:.]*\.)+([^:\/]+)'), copyright_holder TEXT NOT NULL CHECK (octet_length(copyright_holder) >= 1), landing_page TEXT CHECK (landing_page ~* '^[^:]*:\/\/(?:[^\/:]*:[^\/@]*@)?(?:[^\/:.]*\.)+([^:\/]+)'), - lccn INTEGER CHECK(lccn > 0), - oclc INTEGER CHECK (oclc > 0), + lccn TEXT CHECK (octet_length(lccn) >= 1), + oclc TEXT CHECK (octet_length(oclc) >= 1), short_abstract TEXT CHECK (octet_length(short_abstract) >= 1), long_abstract TEXT CHECK (octet_length(long_abstract) >= 1), general_note TEXT CHECK (octet_length(general_note) >= 1), From 332e053d8fcf19d5208bd836c8354cdcf03c727d Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 13:00:12 +0000 Subject: [PATCH 02/22] Update data types --- thoth-api/src/graphql/model.rs | 4 ++-- thoth-api/src/schema.rs | 4 ++-- thoth-api/src/work/model.rs | 12 ++++++------ thoth-app/src/component/new_work.rs | 14 ++++++-------- thoth-app/src/component/work.rs | 14 ++++++-------- thoth-app/src/models/work/create_work_mutation.rs | 8 ++++---- thoth-app/src/models/work/mod.rs | 4 ++-- thoth-app/src/models/work/update_work_mutation.rs | 8 ++++---- 8 files changed, 32 insertions(+), 36 deletions(-) diff --git a/thoth-api/src/graphql/model.rs b/thoth-api/src/graphql/model.rs index c8346e3cf..7713dc501 100644 --- a/thoth-api/src/graphql/model.rs +++ b/thoth-api/src/graphql/model.rs @@ -1375,11 +1375,11 @@ impl Work { self.landing_page.as_ref() } - pub fn lccn(&self) -> Option<&i32> { + pub fn lccn(&self) -> Option<&String> { self.lccn.as_ref() } - pub fn oclc(&self) -> Option<&i32> { + pub fn oclc(&self) -> Option<&String> { self.oclc.as_ref() } diff --git a/thoth-api/src/schema.rs b/thoth-api/src/schema.rs index 619ab4a89..6b6a61715 100644 --- a/thoth-api/src/schema.rs +++ b/thoth-api/src/schema.rs @@ -196,8 +196,8 @@ table! { license -> Nullable, copyright_holder -> Text, landing_page -> Nullable, - lccn -> Nullable, - oclc -> Nullable, + lccn -> Nullable, + oclc -> Nullable, short_abstract -> Nullable, long_abstract -> Nullable, general_note -> Nullable, diff --git a/thoth-api/src/work/model.rs b/thoth-api/src/work/model.rs index 20d5ea1af..2c9234ee5 100644 --- a/thoth-api/src/work/model.rs +++ b/thoth-api/src/work/model.rs @@ -75,8 +75,8 @@ pub struct Work { pub license: Option, pub copyright_holder: String, pub landing_page: Option, - pub lccn: Option, - pub oclc: Option, + pub lccn: Option, + pub oclc: Option, pub short_abstract: Option, pub long_abstract: Option, pub general_note: Option, @@ -113,8 +113,8 @@ pub struct NewWork { pub license: Option, pub copyright_holder: String, pub landing_page: Option, - pub lccn: Option, - pub oclc: Option, + pub lccn: Option, + pub oclc: Option, pub short_abstract: Option, pub long_abstract: Option, pub general_note: Option, @@ -153,8 +153,8 @@ pub struct PatchWork { pub license: Option, pub copyright_holder: String, pub landing_page: Option, - pub lccn: Option, - pub oclc: Option, + pub lccn: Option, + pub oclc: Option, pub short_abstract: Option, pub long_abstract: Option, pub general_note: Option, diff --git a/thoth-app/src/component/new_work.rs b/thoth-app/src/component/new_work.rs index b376ab734..fd580232e 100644 --- a/thoth-app/src/component/new_work.rs +++ b/thoth-app/src/component/new_work.rs @@ -250,8 +250,8 @@ impl Component for NewWorkComponent { license: self.work.license.clone(), copyright_holder: self.work.copyright_holder.clone(), landing_page: self.work.landing_page.clone(), - lccn: self.work.lccn, - oclc: self.work.oclc, + lccn: self.work.lccn.clone(), + oclc: self.work.oclc.clone(), short_abstract: self.work.short_abstract.clone(), long_abstract: self.work.long_abstract.clone(), general_note: self.work.general_note.clone(), @@ -334,11 +334,9 @@ impl Component for NewWorkComponent { self.work.landing_page.neq_assign(Some(landing_page)) } Msg::ChangeLccn(lccn) => { - let lccn: i32 = lccn.parse().unwrap_or(0); self.work.lccn.neq_assign(Some(lccn)) } Msg::ChangeOclc(oclc) => { - let oclc: i32 = oclc.parse().unwrap_or(0); self.work.oclc.neq_assign(Some(oclc)) } Msg::ChangeShortAbstract(short_abstract) => { @@ -487,14 +485,14 @@ impl Component for NewWorkComponent { value=&self.work.doi oninput=self.link.callback(|e: InputData| Msg::ChangeDoi(e.value)) /> - - { - let lccn: i32 = lccn.parse().unwrap_or(0); self.work.lccn.neq_assign(Some(lccn)) } Msg::ChangeOclc(oclc) => { - let oclc: i32 = oclc.parse().unwrap_or(0); self.work.oclc.neq_assign(Some(oclc)) } Msg::ChangeShortAbstract(short_abstract) => { @@ -575,14 +573,14 @@ impl Component for WorkComponent { value=&self.work.doi oninput=self.link.callback(|e: InputData| Msg::ChangeDoi(e.value)) /> - - , pub copyright_holder: String, pub landing_page: Option, - pub lccn: Option, - pub oclc: Option, + pub lccn: Option, + pub oclc: Option, pub short_abstract: Option, pub long_abstract: Option, pub general_note: Option, From b2a6cc0c3b0e54c9db98cb87d37a9a401965e9ee Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 13:05:59 +0000 Subject: [PATCH 03/22] Borrow integer attributes instead of copying them --- thoth-app/src/component/new_work.rs | 14 +++++++------- thoth-app/src/component/work.rs | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/thoth-app/src/component/new_work.rs b/thoth-app/src/component/new_work.rs index fd580232e..dbe7b2720 100644 --- a/thoth-app/src/component/new_work.rs +++ b/thoth-app/src/component/new_work.rs @@ -506,17 +506,17 @@ impl Component for NewWorkComponent {
diff --git a/thoth-app/src/component/work.rs b/thoth-app/src/component/work.rs index b5d0c53b4..2159fa745 100644 --- a/thoth-app/src/component/work.rs +++ b/thoth-app/src/component/work.rs @@ -594,17 +594,17 @@ impl Component for WorkComponent {
From ac6a00d69cd2fbbc6e5def65c1e65c1c772d13dd Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 13:09:08 +0000 Subject: [PATCH 04/22] Apply linting --- thoth-app/src/component/new_work.rs | 8 ++------ thoth-app/src/component/work.rs | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/thoth-app/src/component/new_work.rs b/thoth-app/src/component/new_work.rs index dbe7b2720..cd8e6f079 100644 --- a/thoth-app/src/component/new_work.rs +++ b/thoth-app/src/component/new_work.rs @@ -333,12 +333,8 @@ impl Component for NewWorkComponent { Msg::ChangeLandingPage(landing_page) => { self.work.landing_page.neq_assign(Some(landing_page)) } - Msg::ChangeLccn(lccn) => { - self.work.lccn.neq_assign(Some(lccn)) - } - Msg::ChangeOclc(oclc) => { - self.work.oclc.neq_assign(Some(oclc)) - } + Msg::ChangeLccn(lccn) => self.work.lccn.neq_assign(Some(lccn)), + Msg::ChangeOclc(oclc) => self.work.oclc.neq_assign(Some(oclc)), Msg::ChangeShortAbstract(short_abstract) => { self.work.short_abstract.neq_assign(Some(short_abstract)) } diff --git a/thoth-app/src/component/work.rs b/thoth-app/src/component/work.rs index 2159fa745..5602da8dd 100644 --- a/thoth-app/src/component/work.rs +++ b/thoth-app/src/component/work.rs @@ -401,12 +401,8 @@ impl Component for WorkComponent { Msg::ChangeLandingPage(landing_page) => { self.work.landing_page.neq_assign(Some(landing_page)) } - Msg::ChangeLccn(lccn) => { - self.work.lccn.neq_assign(Some(lccn)) - } - Msg::ChangeOclc(oclc) => { - self.work.oclc.neq_assign(Some(oclc)) - } + Msg::ChangeLccn(lccn) => self.work.lccn.neq_assign(Some(lccn)), + Msg::ChangeOclc(oclc) => self.work.oclc.neq_assign(Some(oclc)), Msg::ChangeShortAbstract(short_abstract) => { self.work.short_abstract.neq_assign(Some(short_abstract)) } From 5973d6ca4b45ed0d65b04cf76423966ab10538bd Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 17:22:05 +0000 Subject: [PATCH 05/22] Fix typo in query name --- thoth-app/src/models/work/works_query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thoth-app/src/models/work/works_query.rs b/thoth-app/src/models/work/works_query.rs index 31080d3e0..b262f0b94 100644 --- a/thoth-app/src/models/work/works_query.rs +++ b/thoth-app/src/models/work/works_query.rs @@ -4,7 +4,7 @@ use serde::Serialize; use super::Work; pub const WORKS_QUERY: &str = " - query PublicationsQuery($limit: Int, $offset: Int, $filter: String) { + query WorksQuery($limit: Int, $offset: Int, $filter: String) { works(limit: $limit, offset: $offset, filter: $filter) { workId workType From 0ab2455e85b7449ed6a42bd4a81755bf98d30107 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 17:46:37 +0000 Subject: [PATCH 06/22] Implement pagination of funders --- thoth-app/src/component/funders.rs | 224 +++++++++++++++---- thoth-app/src/component/fundings_form.rs | 2 + thoth-app/src/models/funder/funders_query.rs | 9 +- thoth-app/src/string.rs | 1 + 4 files changed, 190 insertions(+), 46 deletions(-) diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index e2854763b..2e4485524 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -5,6 +5,7 @@ use yew_router::agent::RouteAgentDispatcher; use yew_router::agent::RouteRequest; use yew_router::prelude::RouterAnchor; use yew_router::route::Route; +use yewtil::fetch::Fetch; use yewtil::fetch::FetchAction; use yewtil::fetch::FetchState; use yewtil::future::LinkFuture; @@ -13,12 +14,24 @@ use crate::component::utils::Loader; use crate::component::utils::Reloader; use crate::models::funder::funders_query::FetchActionFunders; use crate::models::funder::funders_query::FetchFunders; +use crate::models::funder::funders_query::Variables; +use crate::models::funder::funders_query::FundersRequest; +use crate::models::funder::funders_query::FundersRequestBody; use crate::models::funder::Funder; use crate::route::AdminRoute; use crate::route::AppRoute; +use crate::string::NEXT_PAGE_BUTTON; +use crate::string::PAGINATION_COUNT_FUNDERS; +use crate::string::PREVIOUS_PAGE_BUTTON; pub struct FundersComponent { - get_funders: FetchFunders, + limit: i32, + offset: i32, + page_size: i32, + search_term: String, + funders: Vec, + result_count: i32, + fetch_funders: FetchFunders, link: ComponentLink, router: RouteAgentDispatcher<()>, } @@ -26,6 +39,10 @@ pub struct FundersComponent { pub enum Msg { SetFundersFetchState(FetchActionFunders), GetFunders, + PaginateFunders, + SearchFunders(String), + NextPage, + PreviousPage, ChangeRoute(AppRoute), } @@ -34,10 +51,23 @@ impl Component for FundersComponent { type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router = RouteAgentDispatcher::new(); + let router: RouteAgentDispatcher<()> = RouteAgentDispatcher::new(); + let offset: i32 = Default::default(); + let page_size: i32 = 20; + let limit: i32 = Default::default(); + let search_term: String = Default::default(); + let result_count: i32 = Default::default(); + let funders: Vec = Default::default(); + let fetch_funders: FetchFunders = Default::default(); FundersComponent { - get_funders: Default::default(), + limit, + offset, + page_size, + search_term, + funders, + result_count, + fetch_funders, link, router, } @@ -46,7 +76,7 @@ impl Component for FundersComponent { fn rendered(&mut self, first_render: bool) { if first_render { self.link - .send_future(self.get_funders.fetch(Msg::SetFundersFetchState)); + .send_future(self.fetch_funders.fetch(Msg::SetFundersFetchState)); self.link .send_message(Msg::SetFundersFetchState(FetchAction::Fetching)); } @@ -55,16 +85,66 @@ impl Component for FundersComponent { fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { Msg::SetFundersFetchState(fetch_state) => { - self.get_funders.apply(fetch_state); + self.fetch_funders.apply(fetch_state); + self.funders = match self.fetch_funders.as_ref().state() { + FetchState::NotFetching(_) => vec![], + FetchState::Fetching(_) => vec![], + FetchState::Fetched(body) => body.data.funders.clone(), + FetchState::Failed(_, _err) => vec![], + }; + self.result_count = match self.fetch_funders.as_ref().state() { + FetchState::NotFetching(_) => 0, + FetchState::Fetching(_) => 0, + FetchState::Fetched(body) => body.data.funder_count, + FetchState::Failed(_, _err) => 0, + }; true } Msg::GetFunders => { self.link - .send_future(self.get_funders.fetch(Msg::SetFundersFetchState)); + .send_future(self.fetch_funders.fetch(Msg::SetFundersFetchState)); self.link .send_message(Msg::SetFundersFetchState(FetchAction::Fetching)); false } + Msg::PaginateFunders => { + let filter = self.search_term.clone(); + let body = FundersRequestBody { + variables: Variables { + limit: Some(self.limit), + offset: Some(self.offset), + filter: Some(filter), + }, + ..Default::default() + }; + let request = FundersRequest { body }; + self.fetch_funders = Fetch::new(request); + self.link.send_message(Msg::GetFunders); + false + } + Msg::SearchFunders(term) => { + self.limit = self.page_size; + self.offset = 0; + self.search_term = term; + self.link.send_message(Msg::PaginateFunders); + false + } + Msg::NextPage => { + if self.limit < self.result_count && !self.is_next_disabled() { + self.limit += self.page_size; + self.offset += self.page_size; + self.link.send_message(Msg::PaginateFunders); + } + false + } + Msg::PreviousPage => { + if self.offset > 0 && !self.is_previous_disabled() { + self.limit -= self.page_size; + self.offset -= self.page_size; + self.link.send_message(Msg::PaginateFunders); + } + false + } Msg::ChangeRoute(r) => { let route = Route::from(r); self.router.send(RouteRequest::ChangeRoute(route)); @@ -78,47 +158,80 @@ impl Component for FundersComponent { } fn view(&self) -> Html { - match self.get_funders.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - } - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(body) => html! { - <> - + { + match self.fetch_funders.as_ref().state() { + FetchState::NotFetching(_) => { + html! {} + }, + FetchState::Fetching(_) => html! {}, + FetchState::Fetched(_body) => html! { + + + + + + + + + + + { for self.funders.iter().map(|p| self.render_funder(p)) } + +
{ "ID" }{ "Funder" }{ "DOI" }
+ }, + FetchState::Failed(_, err) => html! {&err}, + } + } + } } } @@ -131,6 +244,29 @@ impl FundersComponent { }) } + fn display_count(&self) -> String { + let offset_display = match self.offset == 0 && self.result_count > 0 { + true => 1, + false => self.offset, + }; + let limit_display = match self.limit > self.result_count { + true => self.result_count, + false => self.limit, + }; + format!( + "{} {}-{} of {}", + PAGINATION_COUNT_FUNDERS, offset_display, limit_display, self.result_count + ) + } + + fn is_previous_disabled(&self) -> bool { + self.offset < self.page_size + } + + fn is_next_disabled(&self) -> bool { + self.limit >= self.result_count + } + fn render_funder(&self, f: &Funder) -> Html { html! { , + pub offset: Option, pub filter: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct FundersResponseData { pub funders: Vec, + pub funder_count: i32, } diff --git a/thoth-app/src/string.rs b/thoth-app/src/string.rs index 409886ff1..7dac4304e 100644 --- a/thoth-app/src/string.rs +++ b/thoth-app/src/string.rs @@ -16,6 +16,7 @@ strings! { RELOAD_BUTTON => "Reload", NEXT_PAGE_BUTTON => "Next page", PREVIOUS_PAGE_BUTTON => "Previous", + PAGINATION_COUNT_FUNDERS => "Displaying funders", PAGINATION_COUNT_WORKS => "Displaying works", AUTHENTICATION_ERROR => "Authentication failed", RESPONSE_ERROR => "Failed to obtain a valid response from the server.", From 7573762ed04f275e236616a33e0e997aea8c2791 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Wed, 4 Nov 2020 17:56:21 +0000 Subject: [PATCH 07/22] Use constants for search info strings --- thoth-app/src/component/funders.rs | 3 ++- thoth-app/src/component/works.rs | 3 ++- thoth-app/src/string.rs | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index 2e4485524..ef64cda01 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -23,6 +23,7 @@ use crate::route::AppRoute; use crate::string::NEXT_PAGE_BUTTON; use crate::string::PAGINATION_COUNT_FUNDERS; use crate::string::PREVIOUS_PAGE_BUTTON; +use crate::string::SEARCH_FUNDERS; pub struct FundersComponent { limit: i32, @@ -197,7 +198,7 @@ impl Component for FundersComponent { class="input" type="search" value=self.search_term - placeholder="Search by title, DOI, internal reference, abstract or landing page" + placeholder=SEARCH_FUNDERS oninput=self.link.callback(|e: InputData| Msg::SearchFunders(e.value)) /> diff --git a/thoth-app/src/component/works.rs b/thoth-app/src/component/works.rs index 86287e45c..4d1db486e 100644 --- a/thoth-app/src/component/works.rs +++ b/thoth-app/src/component/works.rs @@ -23,6 +23,7 @@ use crate::route::AppRoute; use crate::string::NEXT_PAGE_BUTTON; use crate::string::PAGINATION_COUNT_WORKS; use crate::string::PREVIOUS_PAGE_BUTTON; +use crate::string::SEARCH_WORKS; pub struct WorksComponent { limit: i32, @@ -187,7 +188,7 @@ impl Component for WorksComponent { class="input" type="search" value=self.search_term - placeholder="Search by title, DOI, internal reference, abstract or landing page" + placeholder=SEARCH_WORKS oninput=self.link.callback(|e: InputData| Msg::SearchWorks(e.value)) /> diff --git a/thoth-app/src/string.rs b/thoth-app/src/string.rs index 7dac4304e..b2b284f9a 100644 --- a/thoth-app/src/string.rs +++ b/thoth-app/src/string.rs @@ -26,4 +26,6 @@ strings! { EMPTY_PUBLICATIONS => "This work does not have any publications. Click above to add associated publications", EMPTY_SUBJECTS => "This work does not have any subjects. Click above to add associated subjects", EMPTY_FUNDINGS => "This work does not have any funding. Click above to add associated funding", + SEARCH_FUNDERS => "Search by name or DOI", + SEARCH_WORKS => "Search by title, DOI, internal reference, abstract or landing page", } From 90c534d085a9f256e91f69bdeb623afca8d15d5a Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 5 Nov 2020 15:27:26 +0000 Subject: [PATCH 08/22] Replace pagination functions with macro --- thoth-app/src/component/funders.rs | 86 +++++++----------------------- thoth-app/src/component/mod.rs | 26 +++++++++ thoth-app/src/component/works.rs | 54 +++++-------------- thoth-app/src/models/funder/mod.rs | 20 +++++++ 4 files changed, 77 insertions(+), 109 deletions(-) diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index ef64cda01..1e6100d97 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -21,7 +21,6 @@ use crate::models::funder::Funder; use crate::route::AdminRoute; use crate::route::AppRoute; use crate::string::NEXT_PAGE_BUTTON; -use crate::string::PAGINATION_COUNT_FUNDERS; use crate::string::PREVIOUS_PAGE_BUTTON; use crate::string::SEARCH_FUNDERS; @@ -47,6 +46,8 @@ pub enum Msg { ChangeRoute(AppRoute), } +pagination_helpers! {FundersComponent, crate::string::PAGINATION_COUNT_FUNDERS} + impl Component for FundersComponent { type Message = Msg; type Properties = (); @@ -55,12 +56,14 @@ impl Component for FundersComponent { let router: RouteAgentDispatcher<()> = RouteAgentDispatcher::new(); let offset: i32 = Default::default(); let page_size: i32 = 20; - let limit: i32 = Default::default(); + let limit: i32 = page_size; let search_term: String = Default::default(); let result_count: i32 = Default::default(); let funders: Vec = Default::default(); let fetch_funders: FetchFunders = Default::default(); + link.send_message(Msg::PaginateFunders); + FundersComponent { limit, offset, @@ -74,38 +77,23 @@ impl Component for FundersComponent { } } - fn rendered(&mut self, first_render: bool) { - if first_render { - self.link - .send_future(self.fetch_funders.fetch(Msg::SetFundersFetchState)); - self.link - .send_message(Msg::SetFundersFetchState(FetchAction::Fetching)); - } - } - fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { Msg::SetFundersFetchState(fetch_state) => { self.fetch_funders.apply(fetch_state); self.funders = match self.fetch_funders.as_ref().state() { - FetchState::NotFetching(_) => vec![], - FetchState::Fetching(_) => vec![], FetchState::Fetched(body) => body.data.funders.clone(), - FetchState::Failed(_, _err) => vec![], + _ => Default::default(), }; self.result_count = match self.fetch_funders.as_ref().state() { - FetchState::NotFetching(_) => 0, - FetchState::Fetching(_) => 0, FetchState::Fetched(body) => body.data.funder_count, - FetchState::Failed(_, _err) => 0, + _ => Default::default(), }; true } Msg::GetFunders => { - self.link - .send_future(self.fetch_funders.fetch(Msg::SetFundersFetchState)); - self.link - .send_message(Msg::SetFundersFetchState(FetchAction::Fetching)); + self.link.send_future(self.fetch_funders.fetch(Msg::SetFundersFetchState)); + self.link.send_message(Msg::SetFundersFetchState(FetchAction::Fetching)); false } Msg::PaginateFunders => { @@ -225,7 +213,16 @@ impl Component for FundersComponent { - { for self.funders.iter().map(|p| self.render_funder(p)) } + { + for self.funders.iter().map(|f| { + let route = f.edit_route().clone(); + f.as_table_row( + self.link.callback(move |_| { + Msg::ChangeRoute(route.clone()) + }) + ) + }) + } }, @@ -236,48 +233,3 @@ impl Component for FundersComponent { } } } - -impl FundersComponent { - fn change_route(&self, app_route: AppRoute) -> Callback { - self.link.callback(move |_| { - let route = app_route.clone(); - Msg::ChangeRoute(route) - }) - } - - fn display_count(&self) -> String { - let offset_display = match self.offset == 0 && self.result_count > 0 { - true => 1, - false => self.offset, - }; - let limit_display = match self.limit > self.result_count { - true => self.result_count, - false => self.limit, - }; - format!( - "{} {}-{} of {}", - PAGINATION_COUNT_FUNDERS, offset_display, limit_display, self.result_count - ) - } - - fn is_previous_disabled(&self) -> bool { - self.offset < self.page_size - } - - fn is_next_disabled(&self) -> bool { - self.limit >= self.result_count - } - - fn render_funder(&self, f: &Funder) -> Html { - html! { - - {&f.funder_id} - {&f.funder_name} - {&f.funder_doi.clone().unwrap_or("".to_string())} - - } - } -} diff --git a/thoth-app/src/component/mod.rs b/thoth-app/src/component/mod.rs index 38fa0b918..49d9fb2d8 100644 --- a/thoth-app/src/component/mod.rs +++ b/thoth-app/src/component/mod.rs @@ -1,3 +1,29 @@ +#[macro_export] +macro_rules! pagination_helpers { + ($component:ident, $pagination_text:expr) => { + impl $component { + fn display_count(&self) -> String { + let offset_display = match self.offset == 0 && self.result_count > 0 { + true => 1, + false => self.offset, + }; + let limit_display = match self.limit > self.result_count { + true => self.result_count, + false => self.limit, + }; + format!("{} {}-{} of {}", $pagination_text, offset_display, limit_display, self.result_count) + } + + fn is_previous_disabled(&self) -> bool { + self.offset < self.page_size + } + + fn is_next_disabled(&self) -> bool { + self.limit >= self.result_count + } + } + } +} pub mod admin; pub mod catalogue; pub mod contributions_form; diff --git a/thoth-app/src/component/works.rs b/thoth-app/src/component/works.rs index 4d1db486e..21414452c 100644 --- a/thoth-app/src/component/works.rs +++ b/thoth-app/src/component/works.rs @@ -21,7 +21,6 @@ use crate::models::work::Work; use crate::route::AdminRoute; use crate::route::AppRoute; use crate::string::NEXT_PAGE_BUTTON; -use crate::string::PAGINATION_COUNT_WORKS; use crate::string::PREVIOUS_PAGE_BUTTON; use crate::string::SEARCH_WORKS; @@ -47,18 +46,20 @@ pub enum Msg { ChangeRoute(AppRoute), } +pagination_helpers! {WorksComponent, crate::string::PAGINATION_COUNT_WORKS} + impl Component for WorksComponent { type Message = Msg; type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { let router = RouteAgentDispatcher::new(); - let offset = 0; - let page_size = 20; - let limit = page_size; - let search_term = "".into(); - let result_count = 0; - let works = vec![]; + let offset: i32 = Default::default(); + let page_size: i32 = 20; + let limit: i32 = page_size; + let search_term: String = Default::default(); + let result_count: i32 = Default::default(); + let works: Vec = Default::default(); link.send_message(Msg::PaginateWorks); @@ -80,24 +81,18 @@ impl Component for WorksComponent { Msg::SetWorksFetchState(fetch_state) => { self.fetch_works.apply(fetch_state); self.works = match self.fetch_works.as_ref().state() { - FetchState::NotFetching(_) => vec![], - FetchState::Fetching(_) => vec![], FetchState::Fetched(body) => body.data.works.clone(), - FetchState::Failed(_, _err) => vec![], + _ => Default::default(), }; self.result_count = match self.fetch_works.as_ref().state() { - FetchState::NotFetching(_) => 0, - FetchState::Fetching(_) => 0, FetchState::Fetched(body) => body.data.work_count, - FetchState::Failed(_, _err) => 0, + _ => Default::default(), }; true } Msg::GetWorks => { - self.link - .send_future(self.fetch_works.fetch(Msg::SetWorksFetchState)); - self.link - .send_message(Msg::SetWorksFetchState(FetchAction::Fetching)); + self.link.send_future(self.fetch_works.fetch(Msg::SetWorksFetchState)); + self.link.send_message(Msg::SetWorksFetchState(FetchAction::Fetching)); false } Msg::PaginateWorks => { @@ -238,28 +233,3 @@ impl Component for WorksComponent { } } } - -impl WorksComponent { - fn display_count(&self) -> String { - let offset_display = match self.offset == 0 && self.result_count > 0 { - true => 1, - false => self.offset, - }; - let limit_display = match self.limit > self.result_count { - true => self.result_count, - false => self.limit, - }; - format!( - "{} {}-{} of {}", - PAGINATION_COUNT_WORKS, offset_display, limit_display, self.result_count - ) - } - - fn is_previous_disabled(&self) -> bool { - self.offset < self.page_size - } - - fn is_next_disabled(&self) -> bool { - self.limit >= self.result_count - } -} diff --git a/thoth-app/src/models/funder/mod.rs b/thoth-app/src/models/funder/mod.rs index 7bb62df73..ef23cae35 100644 --- a/thoth-app/src/models/funder/mod.rs +++ b/thoth-app/src/models/funder/mod.rs @@ -4,6 +4,8 @@ use yew::html; use yew::prelude::Html; use yew::Callback; use yew::MouseEvent; +use crate::route::AdminRoute; +use crate::route::AppRoute; #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] @@ -30,6 +32,24 @@ impl Funder { } } + + pub fn edit_route(&self) -> AppRoute { + AppRoute::Admin(AdminRoute::Funder(self.funder_id.clone())) + } + + pub fn as_table_row(&self, callback: Callback) -> Html { + let funder_doi = self.funder_doi.clone().unwrap_or_else(|| "".to_string()); + html! { + + {&self.funder_id} + {&self.funder_name} + {funder_doi} + + } + } } pub mod create_funder_mutation; From 24ab57e47008077fee53fb9089b7cd1a245c69a7 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 5 Nov 2020 15:31:09 +0000 Subject: [PATCH 09/22] Replace star import --- thoth-app/src/component/funders.rs | 5 ++++- thoth-app/src/component/works.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index 1e6100d97..f054db0ba 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -1,5 +1,8 @@ use yew::html; -use yew::prelude::*; +use yew::prelude::Component; +use yew::prelude::ShouldRender; +use yew::prelude::Html; +use yew::prelude::InputData; use yew::ComponentLink; use yew_router::agent::RouteAgentDispatcher; use yew_router::agent::RouteRequest; diff --git a/thoth-app/src/component/works.rs b/thoth-app/src/component/works.rs index 21414452c..7a6a23df9 100644 --- a/thoth-app/src/component/works.rs +++ b/thoth-app/src/component/works.rs @@ -1,5 +1,8 @@ use yew::html; -use yew::prelude::*; +use yew::prelude::Component; +use yew::prelude::ShouldRender; +use yew::prelude::Html; +use yew::prelude::InputData; use yew::ComponentLink; use yew_router::agent::RouteAgentDispatcher; use yew_router::agent::RouteRequest; From 54562c628fe31927b1874c0a5eb87064f17b22ae Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 5 Nov 2020 18:15:19 +0000 Subject: [PATCH 10/22] Replace component logic with pagination macro --- thoth-app/src/component/funders.rs | 248 ++------------------------ thoth-app/src/component/mod.rs | 269 ++++++++++++++++++++++++++++- thoth-app/src/component/works.rs | 248 ++------------------------ 3 files changed, 300 insertions(+), 465 deletions(-) diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index f054db0ba..7baaf1e2a 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -1,238 +1,22 @@ -use yew::html; -use yew::prelude::Component; -use yew::prelude::ShouldRender; -use yew::prelude::Html; -use yew::prelude::InputData; -use yew::ComponentLink; -use yew_router::agent::RouteAgentDispatcher; -use yew_router::agent::RouteRequest; -use yew_router::prelude::RouterAnchor; -use yew_router::route::Route; -use yewtil::fetch::Fetch; -use yewtil::fetch::FetchAction; -use yewtil::fetch::FetchState; -use yewtil::future::LinkFuture; - -use crate::component::utils::Loader; -use crate::component::utils::Reloader; -use crate::models::funder::funders_query::FetchActionFunders; use crate::models::funder::funders_query::FetchFunders; +use crate::models::funder::funders_query::FundersRequestBody; use crate::models::funder::funders_query::Variables; use crate::models::funder::funders_query::FundersRequest; -use crate::models::funder::funders_query::FundersRequestBody; +use crate::models::funder::funders_query::FetchActionFunders; use crate::models::funder::Funder; -use crate::route::AdminRoute; -use crate::route::AppRoute; -use crate::string::NEXT_PAGE_BUTTON; -use crate::string::PREVIOUS_PAGE_BUTTON; -use crate::string::SEARCH_FUNDERS; - -pub struct FundersComponent { - limit: i32, - offset: i32, - page_size: i32, - search_term: String, - funders: Vec, - result_count: i32, - fetch_funders: FetchFunders, - link: ComponentLink, - router: RouteAgentDispatcher<()>, -} - -pub enum Msg { - SetFundersFetchState(FetchActionFunders), - GetFunders, - PaginateFunders, - SearchFunders(String), - NextPage, - PreviousPage, - ChangeRoute(AppRoute), -} - -pagination_helpers! {FundersComponent, crate::string::PAGINATION_COUNT_FUNDERS} - -impl Component for FundersComponent { - type Message = Msg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router: RouteAgentDispatcher<()> = RouteAgentDispatcher::new(); - let offset: i32 = Default::default(); - let page_size: i32 = 20; - let limit: i32 = page_size; - let search_term: String = Default::default(); - let result_count: i32 = Default::default(); - let funders: Vec = Default::default(); - let fetch_funders: FetchFunders = Default::default(); - - link.send_message(Msg::PaginateFunders); - - FundersComponent { - limit, - offset, - page_size, - search_term, - funders, - result_count, - fetch_funders, - link, - router, - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetFundersFetchState(fetch_state) => { - self.fetch_funders.apply(fetch_state); - self.funders = match self.fetch_funders.as_ref().state() { - FetchState::Fetched(body) => body.data.funders.clone(), - _ => Default::default(), - }; - self.result_count = match self.fetch_funders.as_ref().state() { - FetchState::Fetched(body) => body.data.funder_count, - _ => Default::default(), - }; - true - } - Msg::GetFunders => { - self.link.send_future(self.fetch_funders.fetch(Msg::SetFundersFetchState)); - self.link.send_message(Msg::SetFundersFetchState(FetchAction::Fetching)); - false - } - Msg::PaginateFunders => { - let filter = self.search_term.clone(); - let body = FundersRequestBody { - variables: Variables { - limit: Some(self.limit), - offset: Some(self.offset), - filter: Some(filter), - }, - ..Default::default() - }; - let request = FundersRequest { body }; - self.fetch_funders = Fetch::new(request); - self.link.send_message(Msg::GetFunders); - false - } - Msg::SearchFunders(term) => { - self.limit = self.page_size; - self.offset = 0; - self.search_term = term; - self.link.send_message(Msg::PaginateFunders); - false - } - Msg::NextPage => { - if self.limit < self.result_count && !self.is_next_disabled() { - self.limit += self.page_size; - self.offset += self.page_size; - self.link.send_message(Msg::PaginateFunders); - } - false - } - Msg::PreviousPage => { - if self.offset > 0 && !self.is_previous_disabled() { - self.limit -= self.page_size; - self.offset -= self.page_size; - self.link.send_message(Msg::PaginateFunders); - } - false - } - Msg::ChangeRoute(r) => { - let route = Route::from(r); - self.router.send(RouteRequest::ChangeRoute(route)); - false - } - } - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - html! { - <> - - - { - match self.fetch_funders.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - }, - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(_body) => html! { - - - - - - - - - - { - for self.funders.iter().map(|f| { - let route = f.edit_route().clone(); - f.as_table_row( - self.link.callback(move |_| { - Msg::ChangeRoute(route.clone()) - }) - ) - }) - } - -
{ "ID" }{ "Funder" }{ "DOI" }
- }, - FetchState::Failed(_, err) => html! {&err}, - } - } - - } - } +pagination_component! { + FundersComponent, + Funder, + funders, + funder_count, + FundersRequest, + FetchActionFunders, + FetchFunders, + FundersRequestBody, + Variables, + NewFunder, + SEARCH_FUNDERS, + PAGINATION_COUNT_FUNDERS, + vec!["ID".to_string(), "Funder".to_string(), "DOI".to_string()] } diff --git a/thoth-app/src/component/mod.rs b/thoth-app/src/component/mod.rs index 49d9fb2d8..be7438480 100644 --- a/thoth-app/src/component/mod.rs +++ b/thoth-app/src/component/mod.rs @@ -1,7 +1,20 @@ #[macro_export] macro_rules! pagination_helpers { - ($component:ident, $pagination_text:expr) => { + ($component:ident, $pagination_text:ident, $search_text:ident, $create_route:ident) => { + use crate::string::$pagination_text; + use crate::string::$search_text; + use crate::route::AdminRoute; + use crate::route::AppRoute; + impl $component { + fn search_text(&self) -> String { + format!("{}", $search_text) + } + + fn create_route(&self) -> AppRoute { + AppRoute::Admin(AdminRoute::$create_route) + } + fn display_count(&self) -> String { let offset_display = match self.offset == 0 && self.result_count > 0 { true => 1, @@ -24,6 +37,260 @@ macro_rules! pagination_helpers { } } } + +#[macro_export] +macro_rules! pagination_component { + ( + $component:ident, + $entity:ty, + $result:ident, + $result_count:ident, + $request:ident, + $fetch_action:ty, + $fetch_data:ty, + $request_body:ident, + $request_variables:ident, + $create_route:ident, + $search_text:ident, + $pagination_text:ident, + $table_headers:expr + ) => { + use yew::html; + use yew::prelude::Component; + use yew::ComponentLink; + use yew::prelude::ShouldRender; + use yew::prelude::Html; + use yew::prelude::InputData; + use yew_router::agent::RouteRequest; + use yew_router::agent::RouteAgentDispatcher; + use yew_router::prelude::RouterAnchor; + use yew_router::route::Route; + use yewtil::fetch::Fetch; + use yewtil::fetch::FetchAction; + use yewtil::fetch::FetchState; + use yewtil::future::LinkFuture; + + use crate::component::utils::Loader; + use crate::component::utils::Reloader; + use crate::string::NEXT_PAGE_BUTTON; + use crate::string::PREVIOUS_PAGE_BUTTON; + + pub struct $component { + limit: i32, + offset: i32, + page_size: i32, + search_term: String, + data: Vec<$entity>, + table_headers: Vec, + result_count: i32, + fetch_data: $fetch_data, + link: ComponentLink, + router: RouteAgentDispatcher<()>, + } + + pagination_helpers! {$component, $pagination_text, $search_text, $create_route} + + pub enum Msg { + SetFetchState($fetch_action), + GetData, + PaginateData, + Search(String), + NextPage, + PreviousPage, + ChangeRoute(AppRoute), + } + + impl Component for $component { + type Message = Msg; + type Properties = (); + + fn create(_: Self::Properties, link: ComponentLink) -> Self { + let router = RouteAgentDispatcher::new(); + let offset: i32 = Default::default(); + let page_size: i32 = 20; + let limit: i32 = page_size; + let search_term: String = Default::default(); + let result_count: i32 = Default::default(); + let data = Default::default(); + let fetch_data = Default::default(); + let table_headers = $table_headers; + + link.send_message(Msg::PaginateData); + + $component { + limit, + offset, + page_size, + search_term, + data, + table_headers, + result_count, + fetch_data, + link, + router, + } + } + + fn update(&mut self, msg: Self::Message) -> ShouldRender { + match msg { + Msg::SetFetchState(fetch_state) => { + self.fetch_data.apply(fetch_state); + self.data = match self.fetch_data.as_ref().state() { + FetchState::Fetched(body) => body.data.$result.clone(), + _ => Default::default(), + }; + self.result_count = match self.fetch_data.as_ref().state() { + FetchState::Fetched(body) => body.data.$result_count, + _ => Default::default(), + }; + true + } + Msg::GetData => { + self.link.send_future(self.fetch_data.fetch(Msg::SetFetchState)); + self.link.send_message(Msg::SetFetchState(FetchAction::Fetching)); + false + } + Msg::PaginateData => { + let filter = self.search_term.clone(); + let body = $request_body { + variables: $request_variables { + limit: Some(self.limit), + offset: Some(self.offset), + filter: Some(filter), + }, + ..Default::default() + }; + let request = $request { body }; + self.fetch_data = Fetch::new(request); + self.link.send_message(Msg::GetData); + false + } + Msg::Search(term) => { + self.limit = self.page_size; + self.offset = 0; + self.search_term = term; + self.link.send_message(Msg::PaginateData); + false + } + Msg::NextPage => { + if self.limit < self.result_count && !self.is_next_disabled() { + self.limit += self.page_size; + self.offset += self.page_size; + self.link.send_message(Msg::PaginateData); + } + false + } + Msg::PreviousPage => { + if self.offset > 0 && !self.is_previous_disabled() { + self.limit -= self.page_size; + self.offset -= self.page_size; + self.link.send_message(Msg::PaginateData); + } + false + } + Msg::ChangeRoute(r) => { + let route = Route::from(r); + self.router.send(RouteRequest::ChangeRoute(route)); + false + } + } + } + + fn change(&mut self, _props: Self::Properties) -> ShouldRender { + false + } + + fn view(&self) -> Html { + html! { + <> + + + { + match self.fetch_data.as_ref().state() { + FetchState::NotFetching(_) => { + html! {} + }, + FetchState::Fetching(_) => html! {}, + FetchState::Fetched(_body) => html! { + + + + { + for self.table_headers.iter().map(|h| { + html! {} + }) + } + + + + + { + for self.data.iter().map(|r| { + let route = r.edit_route().clone(); + r.as_table_row( + self.link.callback(move |_| { + Msg::ChangeRoute(route.clone()) + }) + ) + }) + } + +
{h}
+ }, + FetchState::Failed(_, err) => html! {&err}, + } + } + + } + } + + } + } +} + pub mod admin; pub mod catalogue; pub mod contributions_form; diff --git a/thoth-app/src/component/works.rs b/thoth-app/src/component/works.rs index 7a6a23df9..1cc65eb95 100644 --- a/thoth-app/src/component/works.rs +++ b/thoth-app/src/component/works.rs @@ -1,238 +1,22 @@ -use yew::html; -use yew::prelude::Component; -use yew::prelude::ShouldRender; -use yew::prelude::Html; -use yew::prelude::InputData; -use yew::ComponentLink; -use yew_router::agent::RouteAgentDispatcher; -use yew_router::agent::RouteRequest; -use yew_router::prelude::RouterAnchor; -use yew_router::route::Route; -use yewtil::fetch::Fetch; -use yewtil::fetch::FetchAction; -use yewtil::fetch::FetchState; -use yewtil::future::LinkFuture; - -use crate::component::utils::Loader; -use crate::component::utils::Reloader; -use crate::models::work::works_query::FetchActionWorks; use crate::models::work::works_query::FetchWorks; +use crate::models::work::works_query::WorksRequestBody; use crate::models::work::works_query::Variables; use crate::models::work::works_query::WorksRequest; -use crate::models::work::works_query::WorksRequestBody; +use crate::models::work::works_query::FetchActionWorks; use crate::models::work::Work; -use crate::route::AdminRoute; -use crate::route::AppRoute; -use crate::string::NEXT_PAGE_BUTTON; -use crate::string::PREVIOUS_PAGE_BUTTON; -use crate::string::SEARCH_WORKS; - -pub struct WorksComponent { - limit: i32, - offset: i32, - page_size: i32, - search_term: String, - works: Vec, - result_count: i32, - fetch_works: FetchWorks, - link: ComponentLink, - router: RouteAgentDispatcher<()>, -} - -pub enum Msg { - SetWorksFetchState(FetchActionWorks), - GetWorks, - PaginateWorks, - SearchWorks(String), - NextPage, - PreviousPage, - ChangeRoute(AppRoute), -} - -pagination_helpers! {WorksComponent, crate::string::PAGINATION_COUNT_WORKS} - -impl Component for WorksComponent { - type Message = Msg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router = RouteAgentDispatcher::new(); - let offset: i32 = Default::default(); - let page_size: i32 = 20; - let limit: i32 = page_size; - let search_term: String = Default::default(); - let result_count: i32 = Default::default(); - let works: Vec = Default::default(); - - link.send_message(Msg::PaginateWorks); - - WorksComponent { - limit, - offset, - page_size, - search_term, - works, - result_count, - fetch_works: Default::default(), - link, - router, - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetWorksFetchState(fetch_state) => { - self.fetch_works.apply(fetch_state); - self.works = match self.fetch_works.as_ref().state() { - FetchState::Fetched(body) => body.data.works.clone(), - _ => Default::default(), - }; - self.result_count = match self.fetch_works.as_ref().state() { - FetchState::Fetched(body) => body.data.work_count, - _ => Default::default(), - }; - true - } - Msg::GetWorks => { - self.link.send_future(self.fetch_works.fetch(Msg::SetWorksFetchState)); - self.link.send_message(Msg::SetWorksFetchState(FetchAction::Fetching)); - false - } - Msg::PaginateWorks => { - let filter = self.search_term.clone(); - let body = WorksRequestBody { - variables: Variables { - limit: Some(self.limit), - offset: Some(self.offset), - filter: Some(filter), - }, - ..Default::default() - }; - let request = WorksRequest { body }; - self.fetch_works = Fetch::new(request); - self.link.send_message(Msg::GetWorks); - false - } - Msg::SearchWorks(term) => { - self.limit = self.page_size; - self.offset = 0; - self.search_term = term; - self.link.send_message(Msg::PaginateWorks); - false - } - Msg::NextPage => { - if self.limit < self.result_count && !self.is_next_disabled() { - self.limit += self.page_size; - self.offset += self.page_size; - self.link.send_message(Msg::PaginateWorks); - } - false - } - Msg::PreviousPage => { - if self.offset > 0 && !self.is_previous_disabled() { - self.limit -= self.page_size; - self.offset -= self.page_size; - self.link.send_message(Msg::PaginateWorks); - } - false - } - Msg::ChangeRoute(r) => { - let route = Route::from(r); - self.router.send(RouteRequest::ChangeRoute(route)); - false - } - } - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - html! { - <> - - - { - match self.fetch_works.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - }, - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(_body) => html! { - - - - - - - - - - - - - { - for self.works.iter().map(|w| { - let route = w.edit_route().clone(); - w.as_table_row( - self.link.callback(move |_| { - Msg::ChangeRoute(route.clone()) - }) - ) - }) - } - -
{ "ID" }{ "Title" }{ "Type" }{ "Contributors" }{ "DOI" }{ "Publisher" }
- }, - FetchState::Failed(_, err) => html! {&err}, - } - } - - } - } +pagination_component! { + WorksComponent, + Work, + works, + work_count, + WorksRequest, + FetchActionWorks, + FetchWorks, + WorksRequestBody, + Variables, + NewWork, + SEARCH_WORKS, + PAGINATION_COUNT_WORKS, + vec!["ID".to_string(), "Title".to_string(), "Type".to_string(), "Contributors".to_string(), "DOI".to_string(), "Publisher".to_string()] } From e871aa4e4122fb44733e4949fa50be11d63f92f5 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 6 Nov 2020 11:36:07 +0000 Subject: [PATCH 11/22] Add filtering to graphql imprints query --- thoth-api/src/graphql/model.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/thoth-api/src/graphql/model.rs b/thoth-api/src/graphql/model.rs index 7713dc501..4db840057 100644 --- a/thoth-api/src/graphql/model.rs +++ b/thoth-api/src/graphql/model.rs @@ -253,13 +253,19 @@ impl QueryRoot { description = "Query the full list of imprints", arguments( limit(default = 100, description = "The number of items to return"), - offset(default = 0, description = "The number of items to skip") + offset(default = 0, description = "The number of items to skip"), + filter( + default = "".to_string(), + description = "A query string to search. This argument is a test, do not rely on it. At present it simply searches for case insensitive literals on imprint_name and imprint_url" + ), ) )] - fn imprints(context: &Context, limit: i32, offset: i32) -> Vec { + fn imprints(context: &Context, limit: i32, offset: i32, filter: String) -> Vec { use crate::schema::imprint::dsl::*; let connection = context.db.get().unwrap(); imprint + .filter(imprint_name.ilike(format!("%{}%", filter))) + .or_filter(imprint_url.ilike(format!("%{}%", filter))) .order(imprint_name.asc()) .limit(limit.into()) .offset(offset.into()) From 9b477199e206a9bad54c3d386d8857bd6608bedd Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 6 Nov 2020 12:09:13 +0000 Subject: [PATCH 12/22] Add pagination strings for remaining components --- thoth-app/src/string.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/thoth-app/src/string.rs b/thoth-app/src/string.rs index b2b284f9a..3f5573f5f 100644 --- a/thoth-app/src/string.rs +++ b/thoth-app/src/string.rs @@ -18,6 +18,10 @@ strings! { PREVIOUS_PAGE_BUTTON => "Previous", PAGINATION_COUNT_FUNDERS => "Displaying funders", PAGINATION_COUNT_WORKS => "Displaying works", + PAGINATION_COUNT_SERIESES => "Displaying series", + PAGINATION_COUNT_PUBLISHERS => "Displaying publishers", + PAGINATION_COUNT_IMPRINTS => "Displaying imprints", + PAGINATION_COUNT_CONTRIBUTORS => "Displaying contributors", AUTHENTICATION_ERROR => "Authentication failed", RESPONSE_ERROR => "Failed to obtain a valid response from the server.", EMPTY_CONTRIBUTIONS => "This work does not have any contributions. Search contributors above to add its contributions.", @@ -28,4 +32,8 @@ strings! { EMPTY_FUNDINGS => "This work does not have any funding. Click above to add associated funding", SEARCH_FUNDERS => "Search by name or DOI", SEARCH_WORKS => "Search by title, DOI, internal reference, abstract or landing page", + SEARCH_SERIESES => "Search by series name, ISSN or URL", + SEARCH_PUBLISHERS => "Search by publisher name or short name", + SEARCH_IMPRINTS => "Search by imprint name or URL", + SEARCH_CONTRIBUTORS => "Search by name or ORCID", } From 99d0df89ab4d827c36bca4f21310ddf9922201e3 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 6 Nov 2020 12:09:31 +0000 Subject: [PATCH 13/22] Use pagination macro for all remaining components --- thoth-app/src/component/contributions_form.rs | 2 + thoth-app/src/component/contributors.rs | 160 ++--------------- thoth-app/src/component/imprints.rs | 162 ++--------------- thoth-app/src/component/issues_form.rs | 2 + thoth-app/src/component/publishers.rs | 162 ++--------------- thoth-app/src/component/serieses.rs | 164 ++---------------- .../models/contributor/contributors_query.rs | 9 +- thoth-app/src/models/contributor/mod.rs | 21 +++ .../src/models/imprint/imprints_query.rs | 14 +- thoth-app/src/models/imprint/mod.rs | 27 +++ thoth-app/src/models/publisher/mod.rs | 29 ++++ .../src/models/publisher/publishers_query.rs | 14 +- thoth-app/src/models/series/mod.rs | 21 +++ thoth-app/src/models/series/serieses_query.rs | 9 +- 14 files changed, 210 insertions(+), 586 deletions(-) diff --git a/thoth-app/src/component/contributions_form.rs b/thoth-app/src/component/contributions_form.rs index 8d48bedff..3942643cd 100644 --- a/thoth-app/src/component/contributions_form.rs +++ b/thoth-app/src/component/contributions_form.rs @@ -291,6 +291,8 @@ impl Component for ContributionsFormComponent { let body = ContributorsRequestBody { variables: Variables { filter: Some(value), + limit: Some(9999), + ..Default::default() }, ..Default::default() }; diff --git a/thoth-app/src/component/contributors.rs b/thoth-app/src/component/contributors.rs index 4ede1d94e..0904e0aac 100644 --- a/thoth-app/src/component/contributors.rs +++ b/thoth-app/src/component/contributors.rs @@ -1,146 +1,22 @@ -use yew::html; -use yew::prelude::*; -use yew::ComponentLink; -use yew_router::agent::RouteAgentDispatcher; -use yew_router::agent::RouteRequest; -use yew_router::prelude::RouterAnchor; -use yew_router::route::Route; -use yewtil::fetch::FetchAction; -use yewtil::fetch::FetchState; -use yewtil::future::LinkFuture; - -use crate::component::utils::Loader; -use crate::component::utils::Reloader; -use crate::models::contributor::contributors_query::FetchActionContributors; use crate::models::contributor::contributors_query::FetchContributors; +use crate::models::contributor::contributors_query::ContributorsRequestBody; +use crate::models::contributor::contributors_query::Variables; +use crate::models::contributor::contributors_query::ContributorsRequest; +use crate::models::contributor::contributors_query::FetchActionContributors; use crate::models::contributor::Contributor; -use crate::route::AdminRoute; -use crate::route::AppRoute; - -pub struct ContributorsComponent { - markdown: FetchContributors, - link: ComponentLink, - router: RouteAgentDispatcher<()>, -} - -pub enum Msg { - SetMarkdownFetchState(FetchActionContributors), - GetMarkdown, - ChangeRoute(AppRoute), -} - -impl Component for ContributorsComponent { - type Message = Msg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router = RouteAgentDispatcher::new(); - - ContributorsComponent { - markdown: Default::default(), - link, - router, - } - } - - fn rendered(&mut self, first_render: bool) { - if first_render { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetMarkdownFetchState(fetch_state) => { - self.markdown.apply(fetch_state); - true - } - Msg::GetMarkdown => { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - false - } - Msg::ChangeRoute(r) => { - let route = Route::from(r); - self.router.send(RouteRequest::ChangeRoute(route)); - false - } - } - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - match self.markdown.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - } - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(body) => html! { - <> - - - - - - - - - - - { for body.data.contributors.iter().map(|p| self.render_contributor(p)) } - -
{ "ID" }{ "Full Name" }{ "ORCID" }
- - }, - FetchState::Failed(_, err) => html! {&err}, - } - } -} - -impl ContributorsComponent { - fn change_route(&self, app_route: AppRoute) -> Callback { - self.link.callback(move |_| { - let route = app_route.clone(); - Msg::ChangeRoute(route) - }) - } - fn render_contributor(&self, p: &Contributor) -> Html { - html! { - - {&p.contributor_id} - {&p.full_name} - {&p.orcid.clone().unwrap_or("".to_string())} - - } - } +pagination_component! { + ContributorsComponent, + Contributor, + contributors, + contributor_count, + ContributorsRequest, + FetchActionContributors, + FetchContributors, + ContributorsRequestBody, + Variables, + NewContributor, + SEARCH_CONTRIBUTORS, + PAGINATION_COUNT_CONTRIBUTORS, + vec!["ID".to_string(), "FullName".to_string(), "ORCID".to_string()] } diff --git a/thoth-app/src/component/imprints.rs b/thoth-app/src/component/imprints.rs index 47d460210..511dc144d 100644 --- a/thoth-app/src/component/imprints.rs +++ b/thoth-app/src/component/imprints.rs @@ -1,148 +1,22 @@ -use yew::html; -use yew::prelude::*; -use yew::ComponentLink; -use yew_router::agent::RouteAgentDispatcher; -use yew_router::agent::RouteRequest; -use yew_router::prelude::RouterAnchor; -use yew_router::route::Route; -use yewtil::fetch::FetchAction; -use yewtil::fetch::FetchState; -use yewtil::future::LinkFuture; - -use crate::component::utils::Loader; -use crate::component::utils::Reloader; -use crate::models::imprint::imprints_query::FetchActionImprints; use crate::models::imprint::imprints_query::FetchImprints; +use crate::models::imprint::imprints_query::ImprintsRequestBody; +use crate::models::imprint::imprints_query::Variables; +use crate::models::imprint::imprints_query::ImprintsRequest; +use crate::models::imprint::imprints_query::FetchActionImprints; use crate::models::imprint::Imprint; -use crate::route::AdminRoute; -use crate::route::AppRoute; - -pub struct ImprintsComponent { - markdown: FetchImprints, - link: ComponentLink, - router: RouteAgentDispatcher<()>, -} - -pub enum Msg { - SetMarkdownFetchState(FetchActionImprints), - GetMarkdown, - ChangeRoute(AppRoute), -} - -impl Component for ImprintsComponent { - type Message = Msg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router = RouteAgentDispatcher::new(); - - ImprintsComponent { - markdown: Default::default(), - link, - router, - } - } - - fn rendered(&mut self, first_render: bool) { - if first_render { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetMarkdownFetchState(fetch_state) => { - self.markdown.apply(fetch_state); - true - } - Msg::GetMarkdown => { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - false - } - Msg::ChangeRoute(r) => { - let route = Route::from(r); - self.router.send(RouteRequest::ChangeRoute(route)); - false - } - } - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - match self.markdown.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - } - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(body) => html! { - <> - - - - - - - - - - - - { for body.data.imprints.iter().map(|p| self.render_imprint(p)) } - -
{ "ID" }{ "Imprint" }{ "Publisher" }{ "Imprint URL" }
- - }, - FetchState::Failed(_, err) => html! {&err}, - } - } -} - -impl ImprintsComponent { - fn change_route(&self, app_route: AppRoute) -> Callback { - self.link.callback(move |_| { - let route = app_route.clone(); - Msg::ChangeRoute(route) - }) - } - fn render_imprint(&self, p: &Imprint) -> Html { - html! { - - {&p.imprint_id} - {&p.imprint_name} - {&p.publisher.publisher_name} - {&p.imprint_url.clone().unwrap_or("".to_string())} - - } - } +pagination_component! { + ImprintsComponent, + Imprint, + imprints, + imprint_count, + ImprintsRequest, + FetchActionImprints, + FetchImprints, + ImprintsRequestBody, + Variables, + NewImprint, + SEARCH_IMPRINTS, + PAGINATION_COUNT_IMPRINTS, + vec!["ID".to_string(), "Imprint".to_string(), "Publisher".to_string(), "ImprintURL".to_string()] } diff --git a/thoth-app/src/component/issues_form.rs b/thoth-app/src/component/issues_form.rs index 1abef836a..48e94bb4c 100644 --- a/thoth-app/src/component/issues_form.rs +++ b/thoth-app/src/component/issues_form.rs @@ -242,6 +242,8 @@ impl Component for IssuesFormComponent { let body = SeriesesRequestBody { variables: Variables { filter: Some(value), + limit: Some(9999), + ..Default::default() }, ..Default::default() }; diff --git a/thoth-app/src/component/publishers.rs b/thoth-app/src/component/publishers.rs index 56e31c269..c3bca837a 100644 --- a/thoth-app/src/component/publishers.rs +++ b/thoth-app/src/component/publishers.rs @@ -1,148 +1,22 @@ -use yew::html; -use yew::prelude::*; -use yew::ComponentLink; -use yew_router::agent::RouteAgentDispatcher; -use yew_router::agent::RouteRequest; -use yew_router::prelude::RouterAnchor; -use yew_router::route::Route; -use yewtil::fetch::FetchAction; -use yewtil::fetch::FetchState; -use yewtil::future::LinkFuture; - -use crate::component::utils::Loader; -use crate::component::utils::Reloader; -use crate::models::publisher::publishers_query::FetchActionPublishers; use crate::models::publisher::publishers_query::FetchPublishers; +use crate::models::publisher::publishers_query::PublishersRequestBody; +use crate::models::publisher::publishers_query::Variables; +use crate::models::publisher::publishers_query::PublishersRequest; +use crate::models::publisher::publishers_query::FetchActionPublishers; use crate::models::publisher::Publisher; -use crate::route::AdminRoute; -use crate::route::AppRoute; - -pub struct PublishersComponent { - markdown: FetchPublishers, - link: ComponentLink, - router: RouteAgentDispatcher<()>, -} - -pub enum Msg { - SetMarkdownFetchState(FetchActionPublishers), - GetMarkdown, - ChangeRoute(AppRoute), -} - -impl Component for PublishersComponent { - type Message = Msg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router = RouteAgentDispatcher::new(); - - PublishersComponent { - markdown: Default::default(), - link, - router, - } - } - - fn rendered(&mut self, first_render: bool) { - if first_render { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetMarkdownFetchState(fetch_state) => { - self.markdown.apply(fetch_state); - true - } - Msg::GetMarkdown => { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - false - } - Msg::ChangeRoute(r) => { - let route = Route::from(r); - self.router.send(RouteRequest::ChangeRoute(route)); - false - } - } - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - match self.markdown.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - } - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(body) => html! { - <> - - - - - - - - - - - - { for body.data.publishers.iter().map(|p| self.render_publisher(p)) } - -
{ "ID" }{ "Name" }{ "Short Name" }{ "URL" }
- - }, - FetchState::Failed(_, err) => html! {&err}, - } - } -} - -impl PublishersComponent { - fn change_route(&self, app_route: AppRoute) -> Callback { - self.link.callback(move |_| { - let route = app_route.clone(); - Msg::ChangeRoute(route) - }) - } - fn render_publisher(&self, p: &Publisher) -> Html { - html! { - - {&p.publisher_id} - {&p.publisher_name} - {&p.publisher_shortname.clone().unwrap_or("".to_string())} - {&p.publisher_url.clone().unwrap_or("".to_string())} - - } - } +pagination_component! { + PublishersComponent, + Publisher, + publishers, + publisher_count, + PublishersRequest, + FetchActionPublishers, + FetchPublishers, + PublishersRequestBody, + Variables, + NewPublisher, + SEARCH_PUBLISHERS, + PAGINATION_COUNT_PUBLISHERS, + vec!["ID".to_string(), "Name".to_string(), "ShortName".to_string(), "URL".to_string()] } diff --git a/thoth-app/src/component/serieses.rs b/thoth-app/src/component/serieses.rs index 7a3c3a5f2..7b85c06be 100644 --- a/thoth-app/src/component/serieses.rs +++ b/thoth-app/src/component/serieses.rs @@ -1,150 +1,22 @@ -use yew::html; -use yew::prelude::*; -use yew::ComponentLink; -use yew_router::agent::RouteAgentDispatcher; -use yew_router::agent::RouteRequest; -use yew_router::prelude::RouterAnchor; -use yew_router::route::Route; -use yewtil::fetch::FetchAction; -use yewtil::fetch::FetchState; -use yewtil::future::LinkFuture; - -use crate::component::utils::Loader; -use crate::component::utils::Reloader; -use crate::models::series::serieses_query::FetchActionSerieses; use crate::models::series::serieses_query::FetchSerieses; +use crate::models::series::serieses_query::SeriesesRequestBody; +use crate::models::series::serieses_query::Variables; +use crate::models::series::serieses_query::SeriesesRequest; +use crate::models::series::serieses_query::FetchActionSerieses; use crate::models::series::Series; -use crate::route::AdminRoute; -use crate::route::AppRoute; - -pub struct SeriesesComponent { - markdown: FetchSerieses, - link: ComponentLink, - router: RouteAgentDispatcher<()>, -} - -pub enum Msg { - SetMarkdownFetchState(FetchActionSerieses), - GetMarkdown, - ChangeRoute(AppRoute), -} - -impl Component for SeriesesComponent { - type Message = Msg; - type Properties = (); - - fn create(_: Self::Properties, link: ComponentLink) -> Self { - let router = RouteAgentDispatcher::new(); - - SeriesesComponent { - markdown: Default::default(), - link, - router, - } - } - - fn rendered(&mut self, first_render: bool) { - if first_render { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - } - } - - fn update(&mut self, msg: Self::Message) -> ShouldRender { - match msg { - Msg::SetMarkdownFetchState(fetch_state) => { - self.markdown.apply(fetch_state); - true - } - Msg::GetMarkdown => { - self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); - self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); - false - } - Msg::ChangeRoute(r) => { - let route = Route::from(r); - self.router.send(RouteRequest::ChangeRoute(route)); - false - } - } - } - - fn change(&mut self, _props: Self::Properties) -> ShouldRender { - false - } - - fn view(&self) -> Html { - match self.markdown.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - } - FetchState::Fetching(_) => html! {}, - FetchState::Fetched(body) => html! { - <> - - - - - - - - - - - - - { for body.data.serieses.iter().map(|p| self.render_series(p)) } - -
{ "ID" }{ "Series" }{ "Series Type" }{ "ISSN Print" }{ "ISSN Digital" }
- - }, - FetchState::Failed(_, err) => html! {&err}, - } - } -} - -impl SeriesesComponent { - fn change_route(&self, app_route: AppRoute) -> Callback { - self.link.callback(move |_| { - let route = app_route.clone(); - Msg::ChangeRoute(route) - }) - } - fn render_series(&self, p: &Series) -> Html { - html! { - - {&p.series_id} - {&p.series_name} - {&p.series_type} - {&p.issn_print} - {&p.issn_digital} - - } - } +pagination_component! { + SeriesesComponent, + Series, + serieses, + series_count, + SeriesesRequest, + FetchActionSerieses, + FetchSerieses, + SeriesesRequestBody, + Variables, + NewSeries, + SEARCH_SERIESES, + PAGINATION_COUNT_SERIESES, + vec!["ID".to_string(), "Series".to_string(), "SeriesType".to_string(), "ISSNPrint".to_string(), "ISSNDigital".to_string()] } diff --git a/thoth-app/src/models/contributor/contributors_query.rs b/thoth-app/src/models/contributor/contributors_query.rs index 3473ffde5..f20cca559 100644 --- a/thoth-app/src/models/contributor/contributors_query.rs +++ b/thoth-app/src/models/contributor/contributors_query.rs @@ -4,8 +4,8 @@ use serde::Serialize; use super::Contributor; pub const CONTRIBUTORS_QUERY: &str = " - query ContributorsQuery($filter: String) { - contributors(limit: 9999, filter: $filter) { + query ContributorsQuery($limit: Int, $offset: Int, $filter: String) { + contributors(limit: $limit, offset: $offset, filter: $filter) { contributorId firstName lastName @@ -13,6 +13,7 @@ pub const CONTRIBUTORS_QUERY: &str = " orcid website } + contributorCount(filter: $filter) } "; @@ -30,10 +31,14 @@ graphql_query_builder! { #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Variables { + pub limit: Option, + pub offset: Option, pub filter: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct ContributorsResponseData { pub contributors: Vec, + pub contributor_count: i32, } diff --git a/thoth-app/src/models/contributor/mod.rs b/thoth-app/src/models/contributor/mod.rs index bd7b02edd..b577b6405 100644 --- a/thoth-app/src/models/contributor/mod.rs +++ b/thoth-app/src/models/contributor/mod.rs @@ -5,6 +5,9 @@ use yew::prelude::Html; use yew::Callback; use yew::MouseEvent; +use crate::route::AdminRoute; +use crate::route::AppRoute; + #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Contributor { @@ -33,6 +36,24 @@ impl Contributor { } } + + pub fn edit_route(&self) -> AppRoute { + AppRoute::Admin(AdminRoute::Contributor(self.contributor_id.clone())) + } + + pub fn as_table_row(&self, callback: Callback) -> Html { + let orcid = self.orcid.clone().unwrap_or_else(|| "".to_string()); + html! { + + {&self.contributor_id} + {&self.full_name} + {orcid} + + } + } } pub mod contributor_query; diff --git a/thoth-app/src/models/imprint/imprints_query.rs b/thoth-app/src/models/imprint/imprints_query.rs index 08337e069..015e21921 100644 --- a/thoth-app/src/models/imprint/imprints_query.rs +++ b/thoth-app/src/models/imprint/imprints_query.rs @@ -4,8 +4,8 @@ use serde::Serialize; use super::Imprint; const IMPRINTS_QUERY: &str = " - { - imprints(limit: 9999) { + query ImprintsQuery($limit: Int, $offset: Int, $filter: String) { + imprints(limit: $limit, offset: $offset, filter: $filter) { imprintId imprintName imprintUrl @@ -16,6 +16,7 @@ const IMPRINTS_QUERY: &str = " publisherUrl } } + imprintCount(filter: $filter) } "; @@ -31,9 +32,16 @@ graphql_query_builder! { } #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -pub struct Variables {} +#[serde(rename_all = "camelCase")] +pub struct Variables { + pub limit: Option, + pub offset: Option, + pub filter: Option, +} #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct ImprintsResponseData { pub imprints: Vec, + pub imprint_count: i32, } diff --git a/thoth-app/src/models/imprint/mod.rs b/thoth-app/src/models/imprint/mod.rs index f79eb6c47..d3a14014a 100644 --- a/thoth-app/src/models/imprint/mod.rs +++ b/thoth-app/src/models/imprint/mod.rs @@ -1,6 +1,12 @@ use serde::Deserialize; use serde::Serialize; +use yew::html; +use yew::prelude::Html; +use yew::Callback; +use yew::MouseEvent; +use crate::route::AdminRoute; +use crate::route::AppRoute; use super::publisher::Publisher; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -12,6 +18,27 @@ pub struct Imprint { pub publisher: Publisher, } +impl Imprint { + pub fn edit_route(&self) -> AppRoute { + AppRoute::Admin(AdminRoute::Imprint(self.imprint_id.clone())) + } + + pub fn as_table_row(&self, callback: Callback) -> Html { + let imprint_url = self.imprint_url.clone().unwrap_or_else(|| "".to_string()); + html! { + + {&self.imprint_id} + {&self.imprint_name} + {&self.publisher.publisher_name} + {imprint_url} + + } + } +} + impl Default for Imprint { fn default() -> Imprint { Imprint { diff --git a/thoth-app/src/models/publisher/mod.rs b/thoth-app/src/models/publisher/mod.rs index 6587234af..42188344a 100644 --- a/thoth-app/src/models/publisher/mod.rs +++ b/thoth-app/src/models/publisher/mod.rs @@ -1,5 +1,12 @@ use serde::Deserialize; use serde::Serialize; +use yew::html; +use yew::prelude::Html; +use yew::Callback; +use yew::MouseEvent; + +use crate::route::AdminRoute; +use crate::route::AppRoute; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] @@ -10,6 +17,28 @@ pub struct Publisher { pub publisher_url: Option, } +impl Publisher { + pub fn edit_route(&self) -> AppRoute { + AppRoute::Admin(AdminRoute::Publisher(self.publisher_id.clone())) + } + + pub fn as_table_row(&self, callback: Callback) -> Html { + let publisher_shortname = self.publisher_shortname.clone().unwrap_or_else(|| "".to_string()); + let publisher_url = self.publisher_url.clone().unwrap_or_else(|| "".to_string()); + html! { + + {&self.publisher_id} + {&self.publisher_name} + {publisher_shortname} + {publisher_url} + + } + } +} + impl Default for Publisher { fn default() -> Publisher { Publisher { diff --git a/thoth-app/src/models/publisher/publishers_query.rs b/thoth-app/src/models/publisher/publishers_query.rs index 688d99522..81a169af0 100644 --- a/thoth-app/src/models/publisher/publishers_query.rs +++ b/thoth-app/src/models/publisher/publishers_query.rs @@ -4,13 +4,14 @@ use serde::Serialize; use super::Publisher; const PUBLISHERS_QUERY: &str = " - { - publishers(limit: 9999) { + query PublishersQuery($limit: Int, $offset: Int, $filter: String) { + publishers(limit: $limit, offset: $offset, filter: $filter) { publisherId publisherName publisherShortname publisherUrl } + publisherCount(filter: $filter) } "; @@ -26,9 +27,16 @@ graphql_query_builder! { } #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -pub struct Variables {} +#[serde(rename_all = "camelCase")] +pub struct Variables { + pub limit: Option, + pub offset: Option, + pub filter: Option, +} #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct PublishersResponseData { pub publishers: Vec, + pub publisher_count: i32, } diff --git a/thoth-app/src/models/series/mod.rs b/thoth-app/src/models/series/mod.rs index 82a52bea5..64549db3d 100644 --- a/thoth-app/src/models/series/mod.rs +++ b/thoth-app/src/models/series/mod.rs @@ -7,6 +7,8 @@ use yew::Callback; use yew::MouseEvent; use super::imprint::Imprint; +use crate::route::AdminRoute; +use crate::route::AppRoute; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] @@ -57,6 +59,25 @@ impl Series { } } + + pub fn edit_route(&self) -> AppRoute { + AppRoute::Admin(AdminRoute::Series(self.series_id.clone())) + } + + pub fn as_table_row(&self, callback: Callback) -> Html { + html! { + + {&self.series_id} + {&self.series_name} + {&self.series_type} + {&self.issn_print} + {&self.issn_digital} + + } + } } pub mod create_series_mutation; diff --git a/thoth-app/src/models/series/serieses_query.rs b/thoth-app/src/models/series/serieses_query.rs index 065912444..5b522132d 100644 --- a/thoth-app/src/models/series/serieses_query.rs +++ b/thoth-app/src/models/series/serieses_query.rs @@ -4,8 +4,8 @@ use serde::Serialize; use super::Series; pub const SERIESES_QUERY: &str = " - query SeriesesQuery($filter: String) { - serieses(limit: 9999, filter: $filter) { + query SeriesesQuery($limit: Int, $offset: Int, $filter: String) { + serieses(limit: $limit, offset: $offset, filter: $filter) { seriesId seriesType seriesName @@ -23,6 +23,7 @@ pub const SERIESES_QUERY: &str = " } } } + seriesCount(filter: $filter) } "; @@ -40,10 +41,14 @@ graphql_query_builder! { #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Variables { + pub limit: Option, + pub offset: Option, pub filter: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct SeriesesResponseData { pub serieses: Vec, + pub series_count: i32, } From 0a2782e7882d9f1e1b1bd30427f2532e8ee1b283 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 6 Nov 2020 12:35:12 +0000 Subject: [PATCH 14/22] Move catalogue box generation to work implementation --- thoth-app/src/component/catalogue.rs | 151 +------------------------- thoth-app/src/models/work/mod.rs | 154 +++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 150 deletions(-) diff --git a/thoth-app/src/component/catalogue.rs b/thoth-app/src/component/catalogue.rs index c60ffae70..ad7c15454 100644 --- a/thoth-app/src/component/catalogue.rs +++ b/thoth-app/src/component/catalogue.rs @@ -1,4 +1,3 @@ -use std::str::FromStr; use yew::html; use yew::prelude::*; use yew::ComponentLink; @@ -9,9 +8,6 @@ use yewtil::future::LinkFuture; use crate::component::utils::Reloader; use crate::models::work::works_query::FetchActionWorks; use crate::models::work::works_query::FetchWorks; -use crate::models::work::License; -use crate::models::work::Work; -use crate::THOTH_API; pub struct CatalogueComponent { markdown: FetchWorks, @@ -75,155 +71,10 @@ impl Component for CatalogueComponent { }, FetchState::Fetched(body) => html! {
- { for body.data.works.iter().map(render_work) } + { for body.data.works.iter().map(|w| w.as_catalogue_box()) }
}, FetchState::Failed(_, err) => html! {&err}, } } } - -fn render_license(license: &License) -> Html { - html! { - - - { - match license { - License::By =>html!{ - - }, - License::BySa => html!{ - <> - - - - }, - License::ByNd => html!{ - <> - - - - }, - License::ByNc => html!{ - <> - - - - }, - License::ByNcSa => html!{ - <> - - - - - }, - License::ByNcNd => html!{ - <> - - - - - }, - License::Zero => html!{ - - }, - License::Undefined => html! {} - } - } - - } -} - -fn render_work(w: &Work) -> Html { - let doi = &w.doi.clone().unwrap_or_else(|| "".to_string()); - let license = License::from_str(&w.license.clone().unwrap_or_else(|| "".to_string())).unwrap(); - let cover_url = &w.cover_url.clone().unwrap_or_else(|| "".to_string()); - let place = &w.place.clone().unwrap_or_else(|| "".to_string()); - html! { -
-
-
-
- {format!("{} - { render_license(&license) } -
-
-
-
-

- {&w.full_title} -
-

- { - if let Some(contributions) = &w.contributions { - contributions.iter().map(|c| c.main_contribution_item_bullet_small()).collect::() - } else { - html! {} - } - } -
-
- { - if let Some(date) = &w.publication_date { - let mut c1 = date.chars(); - c1.next(); - c1.next(); - c1.next(); - c1.next(); - let year: &str = &date[..date.len() - c1.as_str().len()]; - html! {{place}{": "}{&w.imprint.publisher.publisher_name}{", "}{year}} - } else { - html! {{&w.imprint.publisher.publisher_name}} - } - } -
- {&doi} -

-
- -
-
-
- } -} diff --git a/thoth-app/src/models/work/mod.rs b/thoth-app/src/models/work/mod.rs index a322eccc7..c47ed30d9 100644 --- a/thoth-app/src/models/work/mod.rs +++ b/thoth-app/src/models/work/mod.rs @@ -9,6 +9,7 @@ use yew::prelude::Html; use yew::Callback; use yew::MouseEvent; +use crate::THOTH_API; use super::contribution::Contribution; use super::funding::Funding; use super::imprint::Imprint; @@ -118,6 +119,66 @@ impl Work { AppRoute::Admin(AdminRoute::Work(self.work_id.clone())) } + pub fn onix_endpoint(&self) -> String { + format!("{}/onix/{}", THOTH_API, &self.work_id) + } + + pub fn cover_alt_text(&self) -> String { + format!("{} - Cover Image", &self.title) + } + + pub fn license_icons(&self) -> Html { + let license = License::from_str(&self.license.clone().unwrap_or_else(|| "".to_string())).unwrap(); + html! { + + + { + match license { + License::By =>html!{ + + }, + License::BySa => html!{ + <> + + + + }, + License::ByNd => html!{ + <> + + + + }, + License::ByNc => html!{ + <> + + + + }, + License::ByNcSa => html!{ + <> + + + + + }, + License::ByNcNd => html!{ + <> + + + + + }, + License::Zero => html!{ + + }, + License::Undefined => html! {} + } + } + + } + } + pub fn as_table_row(&self, callback: Callback) -> Html { let doi = self.doi.clone().unwrap_or_else(|| "".to_string()); html! { @@ -142,6 +203,99 @@ impl Work { } } + + pub fn as_catalogue_box(&self) -> Html { + let doi = self.doi.clone().unwrap_or_else(|| "".to_string()); + let cover_url = self.cover_url.clone().unwrap_or_else(|| "".to_string()); + let place = self.place.clone().unwrap_or_else(|| "".to_string()); + html! { +
+
+
+
+ {self.cover_alt_text()} + { self.license_icons() } +
+
+
+
+

+ {&self.full_title} +
+

+ { + if let Some(contributions) = &self.contributions { + contributions.iter().map(|c| c.main_contribution_item_bullet_small()).collect::() + } else { + html! {} + } + } +
+
+ { + if let Some(date) = &self.publication_date { + let mut c1 = date.chars(); + c1.next(); + c1.next(); + c1.next(); + c1.next(); + let year: &str = &date[..date.len() - c1.as_str().len()]; + html! {{place}{": "}{&self.imprint.publisher.publisher_name}{", "}{year}} + } else { + html! {{&self.imprint.publisher.publisher_name}} + } + } +
+ {&doi} +

+
+ +
+
+
+ } + } } impl FromStr for License { From d89c9aeb7d35e72b24f83d1cd1dfce5a112e5015 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 6 Nov 2020 12:50:10 +0000 Subject: [PATCH 15/22] Move create_route method to models --- thoth-app/src/component/contributors.rs | 1 - thoth-app/src/component/funders.rs | 1 - thoth-app/src/component/imprints.rs | 1 - thoth-app/src/component/mod.rs | 13 ++++--------- thoth-app/src/component/publishers.rs | 1 - thoth-app/src/component/serieses.rs | 1 - thoth-app/src/component/works.rs | 1 - thoth-app/src/models/contributor/mod.rs | 4 ++++ thoth-app/src/models/funder/mod.rs | 4 ++++ thoth-app/src/models/imprint/mod.rs | 4 ++++ thoth-app/src/models/publisher/mod.rs | 4 ++++ thoth-app/src/models/series/mod.rs | 4 ++++ thoth-app/src/models/work/mod.rs | 4 ++++ 13 files changed, 28 insertions(+), 15 deletions(-) diff --git a/thoth-app/src/component/contributors.rs b/thoth-app/src/component/contributors.rs index 0904e0aac..b49ecb81b 100644 --- a/thoth-app/src/component/contributors.rs +++ b/thoth-app/src/component/contributors.rs @@ -15,7 +15,6 @@ pagination_component! { FetchContributors, ContributorsRequestBody, Variables, - NewContributor, SEARCH_CONTRIBUTORS, PAGINATION_COUNT_CONTRIBUTORS, vec!["ID".to_string(), "FullName".to_string(), "ORCID".to_string()] diff --git a/thoth-app/src/component/funders.rs b/thoth-app/src/component/funders.rs index 7baaf1e2a..f42e11b71 100644 --- a/thoth-app/src/component/funders.rs +++ b/thoth-app/src/component/funders.rs @@ -15,7 +15,6 @@ pagination_component! { FetchFunders, FundersRequestBody, Variables, - NewFunder, SEARCH_FUNDERS, PAGINATION_COUNT_FUNDERS, vec!["ID".to_string(), "Funder".to_string(), "DOI".to_string()] diff --git a/thoth-app/src/component/imprints.rs b/thoth-app/src/component/imprints.rs index 511dc144d..4843a420a 100644 --- a/thoth-app/src/component/imprints.rs +++ b/thoth-app/src/component/imprints.rs @@ -15,7 +15,6 @@ pagination_component! { FetchImprints, ImprintsRequestBody, Variables, - NewImprint, SEARCH_IMPRINTS, PAGINATION_COUNT_IMPRINTS, vec!["ID".to_string(), "Imprint".to_string(), "Publisher".to_string(), "ImprintURL".to_string()] diff --git a/thoth-app/src/component/mod.rs b/thoth-app/src/component/mod.rs index be7438480..72f3397d1 100644 --- a/thoth-app/src/component/mod.rs +++ b/thoth-app/src/component/mod.rs @@ -1,9 +1,8 @@ #[macro_export] macro_rules! pagination_helpers { - ($component:ident, $pagination_text:ident, $search_text:ident, $create_route:ident) => { + ($component:ident, $pagination_text:ident, $search_text:ident) => { use crate::string::$pagination_text; use crate::string::$search_text; - use crate::route::AdminRoute; use crate::route::AppRoute; impl $component { @@ -11,10 +10,6 @@ macro_rules! pagination_helpers { format!("{}", $search_text) } - fn create_route(&self) -> AppRoute { - AppRoute::Admin(AdminRoute::$create_route) - } - fn display_count(&self) -> String { let offset_display = match self.offset == 0 && self.result_count > 0 { true => 1, @@ -50,7 +45,6 @@ macro_rules! pagination_component { $fetch_data:ty, $request_body:ident, $request_variables:ident, - $create_route:ident, $search_text:ident, $pagination_text:ident, $table_headers:expr @@ -88,7 +82,7 @@ macro_rules! pagination_component { router: RouteAgentDispatcher<()>, } - pagination_helpers! {$component, $pagination_text, $search_text, $create_route} + pagination_helpers! {$component, $pagination_text, $search_text} pub enum Msg { SetFetchState($fetch_action), @@ -201,6 +195,7 @@ macro_rules! pagination_component { } fn view(&self) -> Html { + let route = <$entity>::create_route(); html! { <> - + { self.pagination_controls() } { match self.fetch_data.as_ref().state() { FetchState::NotFetching(_) => { From 4525c0981fce278bfab9dc51b27576b148bb8a81 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Fri, 6 Nov 2020 15:04:56 +0000 Subject: [PATCH 17/22] Allow not using macro function --- thoth-app/src/component/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/thoth-app/src/component/mod.rs b/thoth-app/src/component/mod.rs index 92b3307a1..b979c96fa 100644 --- a/thoth-app/src/component/mod.rs +++ b/thoth-app/src/component/mod.rs @@ -29,6 +29,7 @@ macro_rules! pagination_helpers { self.limit >= self.result_count } + #[allow(dead_code)] fn pagination_controls(&self) -> Html { html! {