diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a58d3d52..3e4efce53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to thoth will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.2.3]](https://github.com/thoth-pub/thoth/releases/tag/v0.2.3) - 2020-11-06 +### Added + - Implemented pagination in all admin components + - Implemented pagination in catalogue + ## [[0.2.2]](https://github.com/thoth-pub/thoth/releases/tag/v0.2.2) - 2020-11-03 ### Changed - Set `THOTH_API` on build via docker diff --git a/Cargo.lock b/Cargo.lock index 79c9a8b0f..5fb05117a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3284,7 +3284,7 @@ dependencies = [ [[package]] name = "thoth" -version = "0.2.2" +version = "0.2.3" dependencies = [ "actix-cors 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "actix-http 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3303,8 +3303,8 @@ dependencies = [ "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)", - "thoth-api 0.2.2", - "thoth-client 0.2.2", + "thoth-api 0.2.3", + "thoth-client 0.2.3", "tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3312,7 +3312,7 @@ dependencies = [ [[package]] name = "thoth-api" -version = "0.2.2" +version = "0.2.3" dependencies = [ "actix-web 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3340,14 +3340,14 @@ dependencies = [ [[package]] name = "thoth-app" -version = "0.2.2" +version = "0.2.3" dependencies = [ "anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", "stdweb 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", - "thoth-api 0.2.2", + "thoth-api 0.2.3", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-logger 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3359,13 +3359,13 @@ dependencies = [ [[package]] name = "thoth-client" -version = "0.2.2" +version = "0.2.3" dependencies = [ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "graphql_client 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", - "thoth-api 0.2.2", + "thoth-api 0.2.3", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index f3a007c94..22c4c650d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth" -version = "0.2.2" +version = "0.2.3" authors = ["Javier Arias "] edition = "2018" license = "Apache-2.0" @@ -16,8 +16,8 @@ maintenance = { status = "actively-developed" } members = ["thoth-api", "thoth-app", "thoth-client"] [dependencies] -thoth-api = {version = "0.2.2", path = "thoth-api", features = ["backend"] } -thoth-client = {version = "0.2.2", path = "thoth-client" } +thoth-api = {version = "0.2.3", path = "thoth-api", features = ["backend"] } +thoth-client = {version = "0.2.3", path = "thoth-client" } actix-http = "1.0.1" actix-rt = "1.0.0" actix-web = "3.0.0" diff --git a/thoth-api/Cargo.toml b/thoth-api/Cargo.toml index 0321aea56..ceff2b6cf 100644 --- a/thoth-api/Cargo.toml +++ b/thoth-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-api" -version = "0.2.2" +version = "0.2.3" authors = ["Javier Arias "] edition = "2018" license = "Apache-2.0" 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), diff --git a/thoth-api/src/graphql/model.rs b/thoth-api/src/graphql/model.rs index c8346e3cf..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()) @@ -1375,11 +1381,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/Cargo.toml b/thoth-app/Cargo.toml index d5dfb3047..878e47499 100644 --- a/thoth-app/Cargo.toml +++ b/thoth-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-app" -version = "0.2.2" +version = "0.2.3" authors = ["Javier Arias "] edition = "2018" license = "Apache-2.0" @@ -30,4 +30,4 @@ wasm-logger = "0.2.0" stdweb = "0.4.20" serde = { version = "1.0.115", features = ["derive"] } url = "2.1.1" -thoth-api = { version = "0.2.2", path = "../thoth-api" } +thoth-api = { version = "0.2.3", path = "../thoth-api" } diff --git a/thoth-app/src/component/catalogue.rs b/thoth-app/src/component/catalogue.rs index c60ffae70..3d55e91ed 100644 --- a/thoth-app/src/component/catalogue.rs +++ b/thoth-app/src/component/catalogue.rs @@ -1,26 +1,47 @@ -use std::str::FromStr; use yew::html; -use yew::prelude::*; +use yew::prelude::Component; +use yew::prelude::FocusEvent; +use yew::prelude::Html; +use yew::prelude::InputData; +use yew::prelude::ShouldRender; use yew::ComponentLink; +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::License; +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::Work; -use crate::THOTH_API; pub struct CatalogueComponent { - markdown: FetchWorks, + limit: i32, + offset: i32, + page_size: i32, + search_term: String, + data: Vec, + result_count: i32, + fetch_data: FetchWorks, link: ComponentLink, } +pagination_helpers! {CatalogueComponent, PAGINATION_COUNT_WORKS, SEARCH_WORKS} + pub enum Msg { - SetMarkdownFetchState(FetchActionWorks), - GetMarkdown, + SetFetchState(FetchActionWorks), + GetData, + PaginateData, + #[allow(dead_code)] + Search(String), + ChangeSearchTerm(String), + TriggerSearch, + NextPage, + PreviousPage, } impl Component for CatalogueComponent { @@ -28,32 +49,90 @@ impl Component for CatalogueComponent { type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { + let offset: i32 = Default::default(); + let page_size: i32 = 10; + 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(); + + link.send_message(Msg::PaginateData); + CatalogueComponent { - markdown: Default::default(), + limit, + offset, + page_size, + search_term, + data, + result_count, + fetch_data, link, } } - 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); + Msg::SetFetchState(fetch_state) => { + self.fetch_data.apply(fetch_state); + self.data = match self.fetch_data.as_ref().state() { + FetchState::Fetched(body) => body.data.works.clone(), + _ => Default::default(), + }; + self.result_count = match self.fetch_data.as_ref().state() { + FetchState::Fetched(body) => body.data.work_count, + _ => Default::default(), + }; true } - Msg::GetMarkdown => { + Msg::GetData => { self.link - .send_future(self.markdown.fetch(Msg::SetMarkdownFetchState)); + .send_future(self.fetch_data.fetch(Msg::SetFetchState)); self.link - .send_message(Msg::SetMarkdownFetchState(FetchAction::Fetching)); + .send_message(Msg::SetFetchState(FetchAction::Fetching)); + false + } + Msg::PaginateData => { + 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_data = Fetch::new(request); + self.link.send_message(Msg::GetData); + false + } + Msg::Search(_) => { + // needed because of macro, but unused here + false + } + Msg::ChangeSearchTerm(term) => { + self.search_term = term; + false + } + Msg::TriggerSearch => { + self.limit = self.page_size; + self.offset = 0; + self.link.send_message(Msg::PaginateData); + false + } + Msg::NextPage => { + if self.limit < self.result_count && !self.is_next_disabled() { + self.offset += self.page_size; + self.link.send_message(Msg::PaginateData); + } + false + } + Msg::PreviousPage => { + if self.offset > 0 && !self.is_previous_disabled() { + self.offset -= self.page_size; + self.link.send_message(Msg::PaginateData); + } false } } @@ -64,166 +143,70 @@ impl Component for CatalogueComponent { } fn view(&self) -> Html { - match self.markdown.as_ref().state() { - FetchState::NotFetching(_) => { - html! {} - } - FetchState::Fetching(_) => html! { -
- { "Loading" } -
- }, - FetchState::Fetched(body) => html! { -
- { for body.data.works.iter().map(render_work) } -
- }, - 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} + html! { +
+
-
-
+ + + + { + match self.fetch_data.as_ref().state() { + FetchState::NotFetching(_) => { + html! {} + } + FetchState::Fetching(_) => html! {}, + FetchState::Fetched(_body) => html! { + { for self.data.iter().map(|w| w.as_catalogue_box()) } + }, + FetchState::Failed(_, err) => html! {&err}, + } + } + + } } } diff --git a/thoth-app/src/component/contributions_form.rs b/thoth-app/src/component/contributions_form.rs index 8d48bedff..b6a8bd141 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() }; @@ -444,7 +446,7 @@ impl Component for ContributionsFormComponent { Msg::CreateContribution }) > - { "Add Subject" } + { "Add Contribution" } + + + + + + + + + } + } } impl FromStr for License { diff --git a/thoth-app/src/models/work/update_work_mutation.rs b/thoth-app/src/models/work/update_work_mutation.rs index 1b9f8934d..688aa43bb 100644 --- a/thoth-app/src/models/work/update_work_mutation.rs +++ b/thoth-app/src/models/work/update_work_mutation.rs @@ -28,8 +28,8 @@ const UPDATE_WORK_MUTATION: &str = " $license: String, $copyrightHolder: String!, $landingPage: String, - $lccn: Int, - $oclc: Int, + $lccn: String, + $oclc: String, $shortAbstract: String, $longAbstract: String, $generalNote: String, @@ -112,8 +112,8 @@ pub struct Variables { 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/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 diff --git a/thoth-app/src/string.rs b/thoth-app/src/string.rs index 409886ff1..3f5573f5f 100644 --- a/thoth-app/src/string.rs +++ b/thoth-app/src/string.rs @@ -16,7 +16,12 @@ strings! { RELOAD_BUTTON => "Reload", NEXT_PAGE_BUTTON => "Next page", 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.", @@ -25,4 +30,10 @@ 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", + 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", } diff --git a/thoth-client/Cargo.toml b/thoth-client/Cargo.toml index 0da775af4..9932eb585 100644 --- a/thoth-client/Cargo.toml +++ b/thoth-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "thoth-client" -version = "0.2.2" +version = "0.2.3" authors = ["Javier Arias "] edition = "2018" license = "Apache-2.0" @@ -9,7 +9,7 @@ repository = "https://github.com/thoth-pub/thoth" readme = "README.md" [dependencies] -thoth-api = {version = "0.2.2", path = "../thoth-api" } +thoth-api = {version = "0.2.3", path = "../thoth-api" } graphql_client = "0.9.0" chrono = { version = "0.4", features = ["serde"] } reqwest = { version = "0.10", features = ["json"] }