Skip to content
This repository has been archived by the owner on Jul 17, 2022. It is now read-only.

Added checkbox for terms and conditions #802

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions db/migrations/003-t&c.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { DataTypes, QueryInterface } from 'sequelize'

export const up = async (queryInterface: QueryInterface) => {
await queryInterface.addColumn(`Groups`, 'termsAndConditionsAcceptedAt', {
allowNull: false,
type: DataTypes.DATE,
})
Comment on lines +4 to +7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

problem: this needs to be a User property, not a Group property.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, what if one user creates multiple groups?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine. In that case they have already accepted the Terms & Conditions. They apply to humans, not to groups. Groups are legally represented by humans, so the humans have to accept the terms.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explanation! I'll work on change and create new PR

}
export const down = async (queryInterface: QueryInterface) => {
await queryInterface.removeColumn(`Groups`, 'termsAndConditionsAcceptedAt')
}
3 changes: 3 additions & 0 deletions db/seeders/20210302050835-demo-groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = {
name: 'Myriam McLaughlin',
}),
captainId,
termsAndConditionsAcceptedAt: new Date(),
},
{
name: 'Brighton',
Expand All @@ -49,6 +50,7 @@ module.exports = {
name: 'Meaghan Crist',
}),
captainId,
termsAndConditionsAcceptedAt: new Date(),
},
{
name: 'Calais',
Expand All @@ -63,6 +65,7 @@ module.exports = {
name: 'Jacinthe Donnelly',
}),
captainId,
termsAndConditionsAcceptedAt: new Date(),
},
],
{},
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/components/TermsAndConditions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { FunctionComponent } from 'react'
import CloseIcon from './alert/CloseIcon'

interface Props {
close: () => void
}

const TermsAndConditions: FunctionComponent<Props> = ({ close }) => {
return (
<div className="bg-gray-300 inset-1/4 fixed text-justify rounded-md">
<div className="absolute top-0 right-0">
<CloseIcon
color="text-black"
onClick={() => {
close()
}}
/>
</div>
<div className="p-4">
<h3 className="text-center text-2xl">Terms & Conditions</h3>
Placeholder text for terms and conitions
</div>
</div>
)
}

export default TermsAndConditions
1 change: 1 addition & 0 deletions frontend/src/pages/groups/GroupCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const GroupCreatePage: FunctionComponent = () => {
onSubmit={onSubmit}
isLoading={mutationIsLoading}
submitButtonLabel="Create group"
renderTermsAndConditions={true}
/>
</main>
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/pages/groups/GroupEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const GroupEditPage: FunctionComponent = () => {
submitButtonLabel="Save changes"
onSubmit={onSubmit}
defaultValues={originalGroupData}
renderTermsAndConditions={false}
/>
</main>
</div>
Expand Down
46 changes: 44 additions & 2 deletions frontend/src/pages/groups/GroupForm.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { FunctionComponent, ReactNode, useEffect } from 'react'
import { FunctionComponent, ReactNode, useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import Button from '../../components/Button'
import SelectField from '../../components/forms/SelectField'
import TextArea from '../../components/forms/TextArea'
import TextField from '../../components/forms/TextField'
import TermsAndConditions from '../../components/TermsAndConditions'
import { GROUP_TYPE_OPTIONS } from '../../data/constants'
import { useAuth } from '../../hooks/useAuth'
import { useCountries } from '../../hooks/useCountries'
import { GroupCreateInput, GroupQuery, GroupType } from '../../types/api-types'
import { stripIdAndTypename } from '../../utils/types'
import TermAndCondCheckbox from './TermAndCondCheckbox'

interface Props {
/**
Expand All @@ -28,6 +30,12 @@ interface Props {
* The callback triggered when the user submits the form
*/
onSubmit: (input: GroupCreateInput) => void

/**
* If true, checkbox for terms and conditions will be displayed
* We don't want to display checkbox terms and conditions on update page
*/
renderTermsAndConditions: boolean
}

/**
Expand All @@ -36,6 +44,8 @@ interface Props {
const GroupForm: FunctionComponent<Props> = (props) => {
const { me: profile } = useAuth()
const countries = useCountries()
const [showTermsAndCond, setShowTermsAndCond] = useState<boolean>(false)
const [timeTcChecked, setTimeTcChecked] = useState<Date | null>(null)

const {
register,
Expand Down Expand Up @@ -67,11 +77,39 @@ const GroupForm: FunctionComponent<Props> = (props) => {
input.groupType = GroupType.Regular
}

if (timeTcChecked !== null) {
input.termsAndConditionsAcceptedAt = timeTcChecked
}

props.onSubmit(input)
})

function handleTcChange() {
if (timeTcChecked === null) {
setTimeTcChecked(new Date())
} else {
setTimeTcChecked(null)
}
}

let termsAndConditions
if (props.renderTermsAndConditions) {
termsAndConditions = (
<TermAndCondCheckbox
handleTcChange={handleTcChange}
timeTcChecked={timeTcChecked}
setShowTermsAndCond={setShowTermsAndCond}
/>
)
} else {
termsAndConditions = null
}

return (
<form onSubmit={submitForm}>
{showTermsAndCond ? (
<TermsAndConditions close={() => setShowTermsAndCond(false)} />
) : null}
<TextField
label="Group name"
name="name"
Expand Down Expand Up @@ -163,13 +201,17 @@ const GroupForm: FunctionComponent<Props> = (props) => {
register={register}
errors={errors}
/>
{termsAndConditions}
</fieldset>

<Button
variant="primary"
type="submit"
className="mt-6"
disabled={props.isLoading}
disabled={
props.isLoading ||
(timeTcChecked === null && props.renderTermsAndConditions)
}
>
{props.submitButtonLabel}
</Button>
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/pages/groups/TermAndCondCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { FunctionComponent } from 'react'
import CheckboxField from '../../components/forms/CheckboxField'

interface Props {
timeTcChecked: Date | null
handleTcChange: () => void
setShowTermsAndCond: (tc: boolean) => void
}

const TermAndCondCheckbox: FunctionComponent<Props> = ({
timeTcChecked,
handleTcChange,
setShowTermsAndCond,
}) => {
return (
<div className="flex flex-row">
<CheckboxField
label=""
checked={timeTcChecked === null ? false : true}
onChange={handleTcChange}
className="cursor-pointer"
/>
<a
className="cursor-pointer text-blue-500"
onClick={() => setShowTermsAndCond(true)}
>
&nbsp;I accept terms and conditions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: use CSS to add spacing, not content.

</a>
</div>
)
}

export default TermAndCondCheckbox
2 changes: 2 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Group {
captainId: Int!
createdAt: DateTime!
updatedAt: DateTime!
termsAndConditionsAcceptedAt: DateTime!
}

input GroupCreateInput {
Expand All @@ -50,6 +51,7 @@ input GroupCreateInput {
primaryLocation: LocationInput!
primaryContact: ContactInfoInput!
website: String
termsAndConditionsAcceptedAt: DateTime!
}

input GroupUpdateInput {
Expand Down
4 changes: 4 additions & 0 deletions src/models/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface GroupAttributes {
primaryContact: ContactInfo
website?: string | null
captainId: number
termsAndConditionsAcceptedAt: Date
}

export interface GroupCreationAttributes
Expand Down Expand Up @@ -63,6 +64,9 @@ export default class Group extends Model<
@BelongsTo(() => UserAccount, 'captainId')
public captain!: UserAccount

@Column
public termsAndConditionsAcceptedAt!: Date

@CreatedAt
@Column
public readonly createdAt!: Date
Expand Down
14 changes: 12 additions & 2 deletions src/resolvers/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Contact } from '../input-validation/Contact'
import { validateIdInput } from '../input-validation/idInputSchema'
import { Location } from '../input-validation/Location'
import {
DateTime,
ID,
NonEmptyShortString,
OptionalValueOrUnset,
Expand All @@ -33,6 +34,7 @@ export const dbToGraphQL = (group: Group): ResolversTypes['Group'] => ({
? undefined
: getCountryByCountryCode(group.primaryLocation.country),
},
termsAndConditionsAcceptedAt: group.termsAndConditionsAcceptedAt,
})

// Group query resolvers
Expand Down Expand Up @@ -108,6 +110,7 @@ export const addGroupInputSchema = Type.Object(
primaryContact: Contact,
website: Type.Optional(URI),
description: Description,
termsAndConditionsAcceptedAt: DateTime,
},
{ additionalProperties: false },
)
Expand Down Expand Up @@ -145,7 +148,12 @@ const addGroup: MutationResolvers['addGroup'] = async (
}

const group = await Group.create({
...valid.value,
...{
...valid.value,
termsAndConditionsAcceptedAt: new Date(
valid.value.termsAndConditionsAcceptedAt,
),
},
captainId: context.auth.userId,
})

Expand Down Expand Up @@ -193,7 +201,9 @@ const updateGroup: MutationResolvers['updateGroup'] = async (
throw new ForbiddenError('Not permitted to update group')
}

const updateAttributes: Partial<Omit<GroupAttributes, 'id'>> = {}
const updateAttributes: Partial<
Omit<GroupAttributes, 'id' | 'termsAndConditionsAcceptedAt'>
> = {}

if (valid.value.name !== undefined) updateAttributes.name = valid.value.name

Expand Down
31 changes: 27 additions & 4 deletions src/tests/groups_api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const commonGroupData = {
primaryLocation: { country: 'FR', city: 'Calais' },
primaryContact: { name: 'Contact', email: '[email protected]' },
website: 'http://www.example.com',
termsAndConditionsAcceptedAt: new Date(),
} as const

describe('Groups API', () => {
Expand Down Expand Up @@ -77,6 +78,7 @@ describe('Groups API', () => {
$primaryLocation: LocationInput!
$primaryContact: ContactInfoInput!
$website: String
$termsAndConditionsAcceptedAt: DateTime!
) {
addGroup(
input: {
Expand All @@ -86,6 +88,7 @@ describe('Groups API', () => {
primaryLocation: $primaryLocation
primaryContact: $primaryContact
website: $website
termsAndConditionsAcceptedAt: $termsAndConditionsAcceptedAt
}
) {
id
Expand All @@ -96,10 +99,28 @@ describe('Groups API', () => {
}
`

const params1 = {
...group1Params,
termsAndConditionsAcceptedAt:
group1Params.termsAndConditionsAcceptedAt.toISOString(),
}

const params2 = {
...group2Params,
termsAndConditionsAcceptedAt:
group2Params.termsAndConditionsAcceptedAt.toISOString(),
}

const daHubParams = {
...daHub,
termsAndConditionsAcceptedAt:
daHub.termsAndConditionsAcceptedAt.toISOString(),
}

it('adds a new group', async () => {
const res = await testServer.executeOperation({
query: ADD_GROUP,
variables: group1Params,
variables: params1,
})

expect(res.errors).toBeUndefined()
Expand All @@ -110,12 +131,12 @@ describe('Groups API', () => {
it('prevents group captains from creating more than 1 group', async () => {
await testServer.executeOperation({
query: ADD_GROUP,
variables: group1Params,
variables: params2,
})

const res = await testServer.executeOperation({
query: ADD_GROUP,
variables: group2Params,
variables: params2,
})

expect(res.errors).not.toBeUndefined()
Expand All @@ -129,7 +150,7 @@ describe('Groups API', () => {
it('allows admins to create DA hubs', async () => {
const res = await adminTestServer.executeOperation({
query: ADD_GROUP,
variables: daHub,
variables: daHubParams,
})

expect(res.errors).toBeUndefined()
Expand All @@ -143,6 +164,8 @@ describe('Groups API', () => {
variables: {
...groupWithDescription,
...commonGroupData,
termsAndConditionsAcceptedAt:
commonGroupData.termsAndConditionsAcceptedAt.toISOString(),
},
})

Expand Down
1 change: 1 addition & 0 deletions src/tests/line_items_api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe('LineItems API', () => {
primaryLocation: { country: 'GB', city: 'Bristol' },
primaryContact: { name: 'Contact', email: '[email protected]' },
captainId: captain.id,
termsAndConditionsAcceptedAt: new Date(),
})

shipment = await Shipment.create({
Expand Down
Loading