From 33dc11ab12f90e46c22d255c38ced2f5e1f31826 Mon Sep 17 00:00:00 2001 From: LionelB Date: Thu, 27 Aug 2020 17:13:35 +0200 Subject: [PATCH 1/3] feat(frontend): paginate alerts --- .../frontend/src/components/button/index.js | 13 +-- .../src/components/changes/ViewDiff.js | 4 +- .../frontend/src/components/changes/index.js | 80 ++++++++--------- .../src/components/pagination/index.js | 85 +++++++++++++++++++ targets/frontend/src/hoc/UserProvider.js | 1 - .../src/pages/alerts/[[...params]].js | 37 ++++++-- targets/frontend/src/theme.js | 68 +++++++++------ 7 files changed, 208 insertions(+), 80 deletions(-) create mode 100644 targets/frontend/src/components/pagination/index.js diff --git a/targets/frontend/src/components/button/index.js b/targets/frontend/src/components/button/index.js index 5cd31c290..cbc1d0c4b 100644 --- a/targets/frontend/src/components/button/index.js +++ b/targets/frontend/src/components/button/index.js @@ -37,7 +37,6 @@ const defaultButtonStyles = { lineHeight: "inherit", m: 0, minWidth: 0, - p: 1, textAlign: "center", textDecoration: "none", }; @@ -51,11 +50,13 @@ const smallSize = { }; // function _Button({ outline = false, ...props }) {} -export const Button = React.forwardRef(({ outline, ...props }, ref) => ( -
- {outline ? : } -
-)); +export const Button = React.forwardRef(({ outline, ...props }, ref) => + outline ? ( + + ) : ( + + ) +); Button.propTypes = { outline: PropTypes.bool, ...buttonPropTypes, diff --git a/targets/frontend/src/components/changes/ViewDiff.js b/targets/frontend/src/components/changes/ViewDiff.js index 6136e5ae8..f008e8a1b 100644 --- a/targets/frontend/src/components/changes/ViewDiff.js +++ b/targets/frontend/src/components/changes/ViewDiff.js @@ -44,7 +44,7 @@ export const ViewDiff = ({ sx, type, inputA, inputB }) => { setMode("words")} + onChange={() => setMode("words")} style={{ marginLeft: 10 }} checked={mode === "words"} />{" "} @@ -52,7 +52,7 @@ export const ViewDiff = ({ sx, type, inputA, inputB }) => { setMode("sentences")} + onChange={() => setMode("sentences")} style={{ marginLeft: 10 }} checked={mode === "sentences"} />{" "} diff --git a/targets/frontend/src/components/changes/index.js b/targets/frontend/src/components/changes/index.js index d7827a8c1..c7e7987cb 100644 --- a/targets/frontend/src/components/changes/index.js +++ b/targets/frontend/src/components/changes/index.js @@ -130,45 +130,47 @@ export function DilaDiffChange({ change }) { {isVisible ? : } )} - + {isVisible && ( + + + {showDiff && ( + <> + Modification du texte + + + )} + {showDiff && showNotaDiff && } + {showNotaDiff && ( + <> + Modification du Nota + + + )} + + + )} ); } diff --git a/targets/frontend/src/components/pagination/index.js b/targets/frontend/src/components/pagination/index.js new file mode 100644 index 000000000..68100751d --- /dev/null +++ b/targets/frontend/src/components/pagination/index.js @@ -0,0 +1,85 @@ +/** @jsx jsx */ +import PropTypes from "prop-types"; +import { Flex, jsx } from "theme-ui"; + +import { Button } from "../button"; +import { Inline } from "../layout/Inline"; +import { Li, List } from "../list"; + +export function Pagination({ + count, + onChange, + offset = 0, + pageSize = 10, + visibleRange = 2, +}) { + const nbPage = Math.ceil(count / pageSize); + const currentPage = Math.floor(offset / pageSize); + + const allPages = Array.from(Array(nbPage)) + .map((_, page) => ({ + index: page, + label: `${page + 1}`, + offset: pageSize * page, + })) + .map((page) => ( +
  • + +
  • + )); + const rangeStartIndex = Math.min( + Math.max(0, currentPage - visibleRange), + nbPage - visibleRange * 2 - 1 + ); + const rangeEndIndex = Math.min( + rangeStartIndex + 1 + visibleRange * 2, + nbPage + ); + + let startPagination = []; + if (rangeStartIndex > 2) { + startPagination = allPages + .slice(0, 1) + .concat(
  • ...
  • ); + } else { + startPagination = allPages.slice(0, 2); + } + + let endPagination = []; + if (nbPage - rangeEndIndex > 2) { + endPagination = [
  • ...
  • ].concat(allPages.slice(-1)); + } else { + endPagination = allPages.slice(-2); + } + + const pages = allPages.slice( + Math.max(rangeStartIndex, 2), + Math.min(rangeEndIndex, nbPage - 2) + ); + + return ( + + + + {startPagination.concat(pages, endPagination)} + + + + ); +} + +Pagination.propTypes = { + count: PropTypes.number.isRequired, + offset: PropTypes.number, + onChange: PropTypes.func.isRequired, + pageSize: PropTypes.number, + visibleRange: PropTypes.number, +}; diff --git a/targets/frontend/src/hoc/UserProvider.js b/targets/frontend/src/hoc/UserProvider.js index 5ef4f9342..0f43d9744 100644 --- a/targets/frontend/src/hoc/UserProvider.js +++ b/targets/frontend/src/hoc/UserProvider.js @@ -35,7 +35,6 @@ function withUserProvider(WrappedComponent) { const componentProps = WrappedComponent.getInitialProps && (await WrappedComponent.getInitialProps(ctx)); - return { ...componentProps }; } render() { diff --git a/targets/frontend/src/pages/alerts/[[...params]].js b/targets/frontend/src/pages/alerts/[[...params]].js index 3c5f2e593..29b8b3ce0 100644 --- a/targets/frontend/src/pages/alerts/[[...params]].js +++ b/targets/frontend/src/pages/alerts/[[...params]].js @@ -5,12 +5,13 @@ import slugify from "@socialgouv/cdtn-slugify"; import { getRouteBySource } from "@socialgouv/cdtn-sources"; import Link from "next/link"; import { useRouter } from "next/router"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { AlertTitle } from "src/components/alerts/AlertTitle"; import { DiffChange } from "src/components/changes"; import { ChangesGroup } from "src/components/changes/ChangeGroup"; import { Layout } from "src/components/layout/auth.layout"; import { Stack } from "src/components/layout/Stack"; +import { Pagination } from "src/components/pagination"; import { TabItem, Tabs } from "src/components/tabs"; import { withCustomUrqlClient } from "src/hoc/CustomUrqlClient"; import { withUserProvider } from "src/hoc/UserProvider"; @@ -19,7 +20,7 @@ import { Card, Container, Divider, jsx, Message, NavLink } from "theme-ui"; import { useQuery } from "urql"; const getAlertQuery = ` -query getAlerts($status: String!, $repository: String!) { +query getAlerts($status: String!, $repository: String!, $limit: Int!, $offset: Int!) { statuses: alert_status { name alerts: alerts_aggregate(where: { @@ -31,7 +32,7 @@ query getAlerts($status: String!, $repository: String!) { __typename } } - alerts(where: { + alerts(limit: $limit, offset: $offset, where: { _and: [ {status: {_eq: $status}}, {repository: {_eq: $repository}}, @@ -50,6 +51,8 @@ query getAlerts($status: String!, $repository: String!) { export function AlertPage() { const router = useRouter(); + const [offset, setOffset] = useState(0); + const pageSize = 10; const [repo, activeStatus = "todo"] = router.query.params; const repository = repo.replace(/_/, "/"); @@ -58,11 +61,15 @@ export function AlertPage() { const [result] = useQuery({ context, query: getAlertQuery, - variables: { repository, status: activeStatus }, + variables: { + limit: pageSize, + offset, + repository, + status: activeStatus, + }, }); const { fetching, error, data } = result; - console.log(" render", result, repository, activeStatus); if (fetching) { return Chargement...; @@ -93,6 +100,7 @@ export function AlertPage() { ).toLocaleDateString()} (${alert.ref})`; } } + return ( @@ -186,8 +194,27 @@ export function AlertPage() { ))} + name === activeStatus).alerts.aggregate + .count + } + pageSize={pageSize} + offset={offset} + onChange={({ offset }) => setOffset(offset)} + /> ); } +/** + * This getInitialProps ensure useState to reset while page url change + * @see https://github.com/vercel/next.js/issues/9992 + */ +AlertPage.getInitialProps = async function ({ query }) { + const [repo, activeStatus = "todo"] = query.params; + return { + key: `${repo}/${activeStatus}`, + }; +}; export default withCustomUrqlClient(withUserProvider(AlertPage)); diff --git a/targets/frontend/src/theme.js b/targets/frontend/src/theme.js index e2fb509dd..d56c64e90 100644 --- a/targets/frontend/src/theme.js +++ b/targets/frontend/src/theme.js @@ -1,5 +1,7 @@ import { darken, rgba, transparentize } from "polished"; +/* eslint-disable sort-keys-fix/sort-keys-fix */ + export const theme = { badges: { circle: { @@ -14,6 +16,11 @@ export const theme = { boxShadow: "inset 0 0 0 1px", color: "primary", }, + accent: { + bg: "accent", + color: "white", + px: "xxsmall", + }, primary: { bg: "primary", color: "white", @@ -37,6 +44,11 @@ export const theme = { bgHover: "highlight", color: "text", }, + accent: { + bg: "accent", + bgHover: "accentHover", + color: "white", + }, primary: { bg: "primary", bgHover: "primaryHover", @@ -61,50 +73,51 @@ export const theme = { padding: "small", }, }, + colors: { accent: "#DA4167", accentHover: darken(0.05, "#DA4167"), + + text: "#3e486e", background: "#fff", + white: "#fff", black: "#232323", - caution: "#FED766", - critical: "#E03616", + link: "#004cce", + linkVisited: "#733d90", focus: rgba("#1e90ff", 0.7), - highlight: "#E6E6EA", info: "#2AB7CA", - link: "#004cce", - linkVisited: "#733d90", - muted: "#717780", + caution: "#FED766", + critical: "#E03616", + positive: "#43AA8B", neutral: "#d1dffd", + muted: "#717780", + highlight: "#E6E6EA", - positive: "#43AA8B", primary: "#f66663", - primaryHover: darken(0.05, "#f66663"), secondary: "#7994d4", - secondaryHover: darken(0.05, "#7994d4"), - text: "#3e486e", - white: "#fff", }, fontSizes: { 0: "0.8rem", - icons: "1.5rem", - large: "1.4rem", - medium: "1rem", + xxsmall: "0.7rem", + xsmall: "0.8rem", small: "0.9rem", + medium: "1rem", + large: "1.4rem", xlarge: "2rem", - xsmall: "0.8rem", xxlarge: "3.2rem", + icons: "1.5rem", }, // fontSizes: [12, 14, 16, 20, 24, 32, 48, 64], fontWeights: { body: 300, - bold: 600, heading: 600, light: 300, regular: 400, semibold: 600, + bold: 600, }, fonts: { body: "muli", @@ -132,34 +145,35 @@ export const theme = { heading: 1.125, }, radii: { - large: "8px", small: "4px", + large: "8px", xlarge: "16px", }, shadows: { card1: "0 0 8px rgba(0, 0, 0, 0.125)", - large: - "0 0 4px 0px rgba(28,28,28,.1), 0 3px 18px -4px rgba(28,28,28,.1), 0 5px 30px -12px rgba(28,28,28,.2)", - medium: - "0 0 4px 0px rgba(28,28,28,.1), 0 8px 8px -4px rgba(28,28,28,.1), 0 12px 12px -8px rgba(28,28,28,.2)", small: "0 0 4px 0px rgba(28,28,28,.1), 0 2px 2px -2px rgba(28,28,28,.1), 0 4px 4px -4px rgba(28,28,28,.2)", + medium: + "0 0 4px 0px rgba(28,28,28,.1), 0 8px 8px -4px rgba(28,28,28,.1), 0 12px 12px -8px rgba(28,28,28,.2)", + large: + "0 0 4px 0px rgba(28,28,28,.1), 0 3px 18px -4px rgba(28,28,28,.1), 0 5px 30px -12px rgba(28,28,28,.2)", }, sizes: { - container: 1440, normal: "xsmall", small: "xxsmall", + container: 1440, }, space: { - large: "2rem", - larger: "4rem", - medium: "1.6rem", + 0: 0, none: "0", + xxsmall: "0.4rem", + xsmall: "0.8rem", small: "1rem", + medium: "1.6rem", + large: "2rem", xlarge: "2.4rem", - xsmall: "0.8rem", xxlarge: "3.2rem", - xxsmall: "0.4rem", + larger: "4rem", }, styles: { hr: { From 4390a3c4ac1c060282c26c1506aefe8b7e101150 Mon Sep 17 00:00:00 2001 From: LionelB Date: Thu, 27 Aug 2020 17:44:10 +0200 Subject: [PATCH 2/3] feat(hasura): order v1 schema views --- .../1598542972178_order-view/down.sql | 6 ++++ .../1598542972178_order-view/up.sql | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 targets/hasura/migrations/1598542972178_order-view/down.sql create mode 100644 targets/hasura/migrations/1598542972178_order-view/up.sql diff --git a/targets/hasura/migrations/1598542972178_order-view/down.sql b/targets/hasura/migrations/1598542972178_order-view/down.sql new file mode 100644 index 000000000..913480aa5 --- /dev/null +++ b/targets/hasura/migrations/1598542972178_order-view/down.sql @@ -0,0 +1,6 @@ + +DROP VIEW IF EXISTS "v1"."legi_data_alerts"; + +DROP VIEW IF EXISTS "v1"."fiche_vdd_alerts"; + +DROP VIEW IF EXISTS "v1"."kali_data_alerts"; diff --git a/targets/hasura/migrations/1598542972178_order-view/up.sql b/targets/hasura/migrations/1598542972178_order-view/up.sql new file mode 100644 index 000000000..71c965aa9 --- /dev/null +++ b/targets/hasura/migrations/1598542972178_order-view/up.sql @@ -0,0 +1,36 @@ + +CREATE OR REPLACE VIEW "v1"."kali_data_alerts" AS + SELECT alerts.id, + alerts.info, + alerts.status, + alerts.repository, + alerts.ref, + alerts.created_at, + alerts.updated_at + FROM alerts + WHERE (alerts.repository = 'socialgouv/kali-data'::text) + ORDER BY alerts.created_at, alerts.info ->> 'num'; + +CREATE OR REPLACE VIEW "v1"."fiche_vdd_alerts" AS + SELECT alerts.id, + alerts.info, + alerts.status, + alerts.repository, + alerts.ref, + alerts.created_at, + alerts.updated_at + FROM alerts + WHERE (alerts.repository = 'socialgouv/fiches-vdd'::text) + ORDER BY alerts.created_at; + +CREATE OR REPLACE VIEW "v1"."legi_data_alerts" AS + SELECT alerts.id, + alerts.info, + alerts.status, + alerts.repository, + alerts.ref, + alerts.created_at, + alerts.updated_at + FROM alerts + WHERE (alerts.repository = 'socialgouv/legi-data'::text) + ORDER BY alerts.created_at; From 3d38289e3d092bcc60524ff1c92c2d96cfb1b3ed Mon Sep 17 00:00:00 2001 From: LionelB Date: Thu, 27 Aug 2020 18:00:45 +0200 Subject: [PATCH 3/3] fix(frontend): hide pagination if not needed --- targets/frontend/src/components/pagination/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/targets/frontend/src/components/pagination/index.js b/targets/frontend/src/components/pagination/index.js index 68100751d..e25f64464 100644 --- a/targets/frontend/src/components/pagination/index.js +++ b/targets/frontend/src/components/pagination/index.js @@ -64,7 +64,9 @@ export function Pagination({ Math.max(rangeStartIndex, 2), Math.min(rangeEndIndex, nbPage - 2) ); - + if (nbPage === 1) { + return null; + } return (