Add revision to new events from new users

This commit is contained in:
Amit Jakubowicz 2019-12-05 09:49:29 +01:00
parent a8849fb1d0
commit cbac3ac64b
9 changed files with 305 additions and 53 deletions

10
@types/graphql.d.ts vendored
View File

@ -27,6 +27,7 @@ declare namespace GQL {
me: IUser | null;
occurrence: IEventOccurrence | null;
occurrences: Array<IEventOccurrence | null> | null;
revisions: Array<IEventRevision | null> | null;
tags: Array<IEventTag | null> | null;
user: IUser | null;
}
@ -47,6 +48,10 @@ declare namespace GQL {
filter: IOccurrencesQueryFilter;
}
interface IRevisionsOnQueryArguments {
filter: IRevisionsQueryFilter;
}
interface IUserOnQueryArguments {
id: string;
}
@ -133,6 +138,7 @@ declare namespace GQL {
dismissedBy: IUser | null;
event: ICalendarEvent;
id: string;
lastChangedAt: any;
submittedAt: any | null;
}
@ -178,6 +184,10 @@ declare namespace GQL {
to?: any | null;
}
interface IRevisionsQueryFilter {
limit?: number | null;
}
interface IMutation {
__typename: 'Mutation';
addEventGalleryImages: ICalendarEvent | null;

View File

@ -4,5 +4,7 @@ import CrossNoIcon from "@material-ui/icons/HighlightOff"
import PlayIcon from "@material-ui/icons/PlayCircleOutline"
import StopIcon from '@material-ui/icons/Stop';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import ReplayIcon from '@material-ui/icons/Replay';
export { TagIcon, CheckIcon, CrossNoIcon, PlayIcon, StopIcon, DeleteForeverIcon }
export { TagIcon, CheckIcon, CrossNoIcon, PlayIcon, StopIcon, DeleteForeverIcon, ReplayIcon }

View File

@ -0,0 +1,84 @@
import styled from "@emotion/styled"
import { ReplayIcon, IconButton, ListItem, ListItemText } from "qpa-components"
import { useMessageCenter } from "qpa-message-center"
import * as React from "react"
import { Link } from "react-router-dom"
import { EventRevisionState } from "../Event/useEventDetailsQuery"
import { PastRevisionData } from "./usePastRevisionsQuery"
import intl from "react-intl-universal"
import useRequestNewRevisionMutation from "./useRequestNewRevisionMutation"
import css from "@emotion/css"
interface Props {
revision: PastRevisionData
language: string
onRefetchPendingEvents: () => void
}
const PastRevisionListItem = (props: Props) => {
const event = props.revision.event
const { notifyError } = useMessageCenter()
const languageIndex = event.infos.findIndex(
info => info.language === props.language
)
const [
requestNewRevision,
{
data: requestNewRevisionData,
loading: requestNewRevisionLoading,
error: requestNewRevisionError,
},
] = useRequestNewRevisionMutation({
variables: {
input: { eventId: event.id },
},
onError: notifyError,
})
const eventInfo = props.revision.event.infos[languageIndex || 0]
const eventIsPendingRevision = [
EventRevisionState.PENDING_SUGGESTED_REVISION,
EventRevisionState.PENDING_MANDATORY_REVISION,
].includes(event.revisionState)
return (
<ListItem>
<ListItemText>
<Link to={`/e/aaa/${event.id}`}>{eventInfo.title}</Link>
<div>
{props.revision.dismissedBy
? intl.get("dismissed-by", {
name: props.revision.dismissedBy.name,
})
: props.revision.conclusion}
</div>
<div
css={css`
font-size: 12px;
`}
>
{intl.get("last-change")}: {props.revision.lastChangedAt}
</div>
</ListItemText>
<ActionItems>
<IconButton
edge="end"
aria-label="request new revision"
label={intl.get("request-new-revision")}
onClick={() => requestNewRevision()}
disabled={eventIsPendingRevision}
>
<ReplayIcon />
</IconButton>
</ActionItems>
</ListItem>
)
}
const ActionItems = styled.div`
display: flex;
flex-direction: row;
> *:not(:first-of-type) {
margin-left: 8px;
}
`
export default PastRevisionListItem

View File

@ -1,8 +1,16 @@
{
"en-GB": {
"revision-successful": "Revision was submitted successfully"
"revision-successful": "Revision was submitted successfully",
"events-pending-revision": "Events pending revision",
"dismissed-by": "Dismissed by {name}",
"request-new-revision": "Resubmit",
"last-change": "Last change"
},
"es-ES": {
"revision-successful": "Event ha sido revisado correctamente"
"revision-successful": "Event ha sido revisado correctamente",
"events-pending-revision": "Eventos pendientes a revisar",
"dismissed-by": "Anulado por {name}",
"request-new-revision": "Solicitar",
"last-change": "Última actualisación"
}
}

View File

@ -1,47 +1,106 @@
import {Checkbox, ListItem, ListItemSecondaryAction, ListItemText, Spinner} from "qpa-components"
import {List} from "qpa-components/List"
import * as React from 'react'
import {RouteComponentProps, withRouter} from "react-router"
import {useAppContext} from "../Context/AppContext"
import {
Checkbox,
ListItem,
ListItemSecondaryAction,
ListItemText,
Spinner,
} from "qpa-components"
import { List } from "qpa-components/List"
import * as React from "react"
import { RouteComponentProps, withRouter } from "react-router"
import { useAppContext } from "../Context/AppContext"
import PastRevisionListItem from "./PastRevisionListItem"
import ReviseListItem from "./ReviseListItem"
import useEventsPendingRevisionQuery from "./useEventsPendingRevisionQuery"
import messages from './ReviseEvents.msg.json'
import intl from 'react-intl-universal'
import messages from "./ReviseEvents.msg.json"
import intl from "react-intl-universal"
import styled from "@emotion/styled"
import usePastRevisionsQuery from "./usePastRevisionsQuery"
interface Props extends RouteComponentProps {
}
interface Props extends RouteComponentProps {}
const ReviseEvents = (props: Props) => {
intl.load(messages)
const {data,loading,error} = useEventsPendingRevisionQuery()
const { language } = useAppContext()
const [selectedIds, setSelectedIds] = React.useState(new Set<string>())
if (loading) {
return <Spinner />
}
if (error) {
return <p>{error.message}</p>
}
return <List>
{
data.events.map(event => {
const isChecked = selectedIds.has(event.id)
return (
<ReviseListItem key={event.id} isChecked={isChecked} event={event} language={language} onCheckedChange={() => {
const newSelectedIds = new Set(selectedIds)
if (isChecked) {
newSelectedIds.delete(event.id)
} else {
newSelectedIds.add(event.id)
}
setSelectedIds(newSelectedIds)
}}/>
)
})
}
</List>
intl.load(messages)
const {
data: pendingEventsData,
loading: pendingEventsLoading,
error: pendingEventsError,
refetch: pendingEventsRefetch,
} = useEventsPendingRevisionQuery()
const {
data: pastRevisionsData,
loading: pastRevisionsLoading,
error: pastRevisionsError,
} = usePastRevisionsQuery()
const { language } = useAppContext()
const [selectedIds, setSelectedIds] = React.useState(new Set<string>())
if (pendingEventsLoading) {
return <Spinner />
}
if (pendingEventsError) {
return <p>{pendingEventsError.message}</p>
}
return (
<Root>
<Section>
<Title>{intl.get("events-pending-revision")}</Title>
{pendingEventsLoading ? (
<Spinner />
) : (
<List>
{pendingEventsData.events.map(event => {
const isChecked = selectedIds.has(event.id)
return (
<ReviseListItem
key={event.id}
isChecked={isChecked}
event={event}
language={language}
onCheckedChange={() => {
const newSelectedIds = new Set(selectedIds)
if (isChecked) {
newSelectedIds.delete(event.id)
} else {
newSelectedIds.add(event.id)
}
setSelectedIds(newSelectedIds)
}}
/>
)
})}
</List>
)}
</Section>
<Section>
<Title>Past revisions</Title>
{pastRevisionsLoading ? (
<Spinner />
) : (
<List>
{pastRevisionsData.revisions?.map(revision => {
return (
<PastRevisionListItem
revision={revision}
language={language}
key={revision.id}
onRefetchPendingEvents={() => pendingEventsRefetch()}
/>
)
})}
</List>
)}
</Section>
</Root>
)
}
const Root = styled.div`
padding: 24px;
`
const Section = styled.section``
const Title = styled.div`
color: rgba(0, 0, 0, 0.8);
font-size: 1.5em;
`
export default withRouter(ReviseEvents)

View File

@ -1,7 +1,25 @@
import { QueryHookOptions, useQuery } from "@apollo/react-hooks"
import gql from "graphql-tag"
export const RevisionFragment = gql`
fragment RevisionData on EventRevision {
id
author {
id
name
}
createdAt
submittedAt
conclusion
lastChangedAt
dismissedBy {
id
name
}
}
`
export const RevisionPendingEventFragment = gql`
${RevisionFragment}
fragment PendingEvent on CalendarEvent {
id
infos {
@ -10,17 +28,7 @@ export const RevisionPendingEventFragment = gql`
}
revisionState
revisions {
id
author {
id
name
}
createdAt
submittedAt
dismissedBy {
id
name
}
...RevisionData
}
}
`
@ -32,7 +40,7 @@ const query = gql`
}
}
`
interface RevisionData {
export interface RevisionData {
id: string
author: {
id: string
@ -40,6 +48,8 @@ interface RevisionData {
}
createdAt: Date
submittedAt?: Date
lastChangedAt: Date
conclusion?: string
dismissedBy?: RevisionDismissingUserData
}
interface RevisionDismissingUserData {

View File

@ -0,0 +1,48 @@
import {QueryHookOptions, useQuery} from "@apollo/react-hooks"
import gql from "graphql-tag"
import {EventRevisionState} from "../Event/useEventDetailsQuery"
import {RevisionData, RevisionFragment} from "./useEventsPendingRevisionQuery"
const query = gql`
${RevisionFragment}
query PastRevisions($limit: Int) {
revisions(filter: {limit: $limit}) {
...RevisionData
dismissedBy {
id
name
}
event {
id
revisionState
infos {
title
language
}
}
}
}
`
export interface PastRevisionData extends RevisionData{
dismissedBy?: {
id: string
name: string
}
event: {
revisionState: EventRevisionState
id: string
infos: Array<{
language: string
title: string
}>
}
}
interface Data {
revisions: PastRevisionData[]
}
interface Variables {
limit?: number
}
const usePastRevisionsQuery = (options?: QueryHookOptions<Data, Variables>) => useQuery<Data,Variables>(query, options)
export default usePastRevisionsQuery

View File

@ -0,0 +1,25 @@
import { QueryHookOptions, useMutation } from "@apollo/react-hooks"
import gql from "graphql-tag"
import {
RevisionPendingEventFragment,
RevisionPendingEventData,
} from "./useEventsPendingRevisionQuery"
const mutation = gql`
${RevisionPendingEventFragment}
mutation RequestNewRevision($input: RequestRevisionInput!) {
requestEventRevision(input: $input) {
...PendingEvent
}
}
`
interface Data extends RevisionPendingEventData {}
interface Variables {
input: GQL.IRequestRevisionInput
}
const useRequestNewRevisionMutation = (
options?: QueryHookOptions<Data, Variables>
) => useMutation<Data, Variables>(mutation, options)
export default useRequestNewRevisionMutation

View File

@ -60,6 +60,7 @@ type EventRevision {
dismissedBy: User
event: CalendarEvent!
id: ID!
lastChangedAt: Date!
submittedAt: Date
}
@ -117,6 +118,7 @@ type Query {
me: User
occurrence(id: ID!): EventOccurrence
occurrences(filter: OccurrencesQueryFilter!): [EventOccurrence]
revisions(filter: RevisionsQueryFilter!): [EventRevision]
tags: [EventTag]
user(id: ID!): User
}
@ -241,6 +243,10 @@ input ReviseEventInput {
revisionId: ID!
}
input RevisionsQueryFilter {
limit: Int
}
input RevokeRoleInput {
roleType: RoleType!
userId: ID!