invites page
This commit is contained in:
		
							parent
							
								
									4935c7dc1c
								
							
						
					
					
						commit
						7107d329ba
					
				| @ -28,10 +28,22 @@ export default { | ||||
|       return await models.invite.create({ | ||||
|         data: { gift, limit, userId: me.id } | ||||
|       }) | ||||
|     }, | ||||
|     revokeInvite: async (parent, { id }, { me, models }) => { | ||||
|       if (!me) { | ||||
|         throw new AuthenticationError('you must be logged in') | ||||
|       } | ||||
| 
 | ||||
|       return await models.invite.update({ | ||||
|         where: { id }, | ||||
|         data: { revoked: true } | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   Invite: { | ||||
|     invitees: async (invite, args, { models }) => [] | ||||
|     invitees: async (invite, args, { me, models }) => { | ||||
|       return await models.user.findMany({ where: { inviteId: invite.id } }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,7 @@ export default gql` | ||||
| 
 | ||||
|   extend type Mutation { | ||||
|     createInvite(gift: Int!, limit: Int): Invite | ||||
|     revokeInvite(id: ID!): Invite | ||||
|   } | ||||
| 
 | ||||
|   type Invite { | ||||
|  | ||||
| @ -3,15 +3,17 @@ import ArrowRight from '../svgs/arrow-right-s-fill.svg' | ||||
| import ArrowDown from '../svgs/arrow-down-s-fill.svg' | ||||
| import { useEffect, useState } from 'react' | ||||
| 
 | ||||
| export default function AccordianItem ({ header, body, headerColor = 'grey' }) { | ||||
|   const [open, setOpen] = useState(false) | ||||
| export default function AccordianItem ({ header, body, headerColor = 'grey', show }) { | ||||
|   const [open, setOpen] = useState(show) | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     setOpen(false) | ||||
|     setOpen(show) | ||||
|   }, []) | ||||
| 
 | ||||
|   return ( | ||||
|     <Accordion> | ||||
|     <Accordion | ||||
|       defaultActiveKey={show ? '0' : undefined} | ||||
|     > | ||||
|       <Accordion.Toggle | ||||
|         as={props => <div {...props} />} | ||||
|         eventKey='0' | ||||
|  | ||||
| @ -62,6 +62,10 @@ export default function Header () { | ||||
|               <Link href='/wallet' passHref> | ||||
|                 <NavDropdown.Item>wallet</NavDropdown.Item> | ||||
|               </Link> | ||||
|               <NavDropdown.Divider /> | ||||
|               <Link href='/invites' passHref> | ||||
|                 <NavDropdown.Item>invites</NavDropdown.Item> | ||||
|               </Link> | ||||
|               <div> | ||||
|                 <NavDropdown.Divider /> | ||||
|                 <RefreshableLink href='/recent' passHref> | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| import Layout from '../../components/layout' | ||||
| import * as Yup from 'yup' | ||||
| import { Form, Input, SubmitButton } from '../../components/form' | ||||
| import { CopyInput, Form, Input, SubmitButton } from '../../components/form' | ||||
| import { InputGroup } from 'react-bootstrap' | ||||
| import { gql, useMutation, useQuery } from '@apollo/client' | ||||
| import { INVITE_FIELDS } from '../../fragments/invites' | ||||
| import AccordianItem from '../../components/accordian-item' | ||||
| import styles from '../../styles/invites.module.css' | ||||
| 
 | ||||
| export const InviteSchema = Yup.object({ | ||||
|   gift: Yup.number().typeError('must be a number') | ||||
| @ -67,11 +69,74 @@ function InviteForm () { | ||||
|         name='limit' | ||||
|       /> | ||||
| 
 | ||||
|       <SubmitButton variant='secondary' className='mt-2'>create</SubmitButton> | ||||
|       <SubmitButton variant='secondary'>create</SubmitButton> | ||||
|     </Form> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| function Invite ({ invite, active }) { | ||||
|   const [revokeInvite] = useMutation( | ||||
|     gql` | ||||
|       ${INVITE_FIELDS} | ||||
|       mutation revokeInvite($id: ID!) { | ||||
|         revokeInvite(id: $id) { | ||||
|           ...InviteFields | ||||
|         } | ||||
|       }` | ||||
|   ) | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       className={styles.invite} | ||||
|     > | ||||
|       <CopyInput | ||||
|         groupClassName='mb-1' | ||||
|         size='sm' type='text' | ||||
|         placeholder={`https://stacker.news/invites/${invite.id}`} readOnly | ||||
|       /> | ||||
|       <div className={styles.other}> | ||||
|         <span>{invite.invitees.length} joined{invite.limit ? ` of ${invite.limit}` : ''}</span> | ||||
|         {active | ||||
|           ? ( | ||||
|             <> | ||||
|               <span> \ </span> | ||||
|               <span | ||||
|                 className={styles.revoke} | ||||
|                 onClick={() => revokeInvite({ variables: { id: invite.id } })} | ||||
|               >revoke | ||||
|               </span> | ||||
|             </>) | ||||
| 
 | ||||
|           : invite.revoked && ( | ||||
|             <> | ||||
|               <span> \ </span> | ||||
|               <span | ||||
|                 className='text-danger' | ||||
|               >revoked | ||||
|               </span> | ||||
|             </>)} | ||||
|       </div> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| function InviteList ({ name, invites }) { | ||||
|   return ( | ||||
|     <div className='mt-4'> | ||||
|       <AccordianItem | ||||
|         show | ||||
|         headerColor='#212529' | ||||
|         header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>{name}</div>} body={ | ||||
|           <div className={styles.invites}>{invites.map(invite => { | ||||
|             return <Invite invite={invite} key={invite.id} active={name === 'active'} /> | ||||
|           })} | ||||
|           </div> | ||||
|       } | ||||
|       /> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| export default function Invites () { | ||||
|   const { data } = useQuery( | ||||
|     gql` | ||||
| @ -82,12 +147,20 @@ export default function Invites () { | ||||
|         } | ||||
|       } | ||||
|     `)
 | ||||
| 
 | ||||
|   const [active, inactive] = data && data.invites | ||||
|     ? data.invites.reduce((result, invite) => { | ||||
|         result[invite.revoked || (invite.limit && invite.invitees.length >= invite.limit) ? 1 : 0].push(invite) | ||||
|         return result | ||||
|       }, | ||||
|       [[], []]) | ||||
|     : [[], []] | ||||
| 
 | ||||
|   return ( | ||||
|     <Layout> | ||||
|       <InviteForm /> | ||||
|       {data && data.invites && data.invites.map(invite => { | ||||
|         return <div key={invite.id}>{invite.id}</div> | ||||
|       })} | ||||
|       {active.length > 0 && <InviteList name='active' invites={active} />} | ||||
|       {inactive.length > 0 && <InviteList name='inactive' invites={inactive} />} | ||||
|     </Layout> | ||||
|   ) | ||||
| } | ||||
|  | ||||
							
								
								
									
										27
									
								
								styles/invites.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								styles/invites.module.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| .invites { | ||||
|     background-color: rgba(0, 0, 0, 0.06); | ||||
|     border-radius: .4rem; | ||||
|     padding: .75rem; | ||||
| } | ||||
| 
 | ||||
| .invites { | ||||
|     margin-bottom: 0 !important; | ||||
| } | ||||
| 
 | ||||
| .other { | ||||
|     font-weight: 500; | ||||
|     font-size: 70%; | ||||
|     color: grey; | ||||
| } | ||||
| 
 | ||||
| .revoke { | ||||
|     cursor: pointer; | ||||
| } | ||||
| 
 | ||||
| .invite { | ||||
|     margin-bottom: .5rem; | ||||
| } | ||||
| 
 | ||||
| .invite:last-child { | ||||
|     margin-bottom: 0; | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user