Skip to content

Commit 0ea0c8d

Browse files
authored
Merge pull request #867 from PayButton/feat/orgs
[#543] feat: organizations
2 parents 87f07d7 + 374157d commit 0ea0c8d

23 files changed

Lines changed: 1318 additions & 22 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useForm } from 'react-hook-form'
2+
import { UserWithSupertokens } from 'services/userService'
3+
import style from './organization.module.css'
4+
5+
interface IProps {
6+
user: UserWithSupertokens
7+
setError: Function
8+
setOrg: Function
9+
setOrgMembers: Function
10+
}
11+
12+
interface CreateOrganizationForm {
13+
name: string
14+
userId: string
15+
}
16+
17+
const CreateOrganization = ({ user, setError, setOrg, setOrgMembers }: IProps): JSX.Element => {
18+
const { register, handleSubmit } = useForm<CreateOrganizationForm>({
19+
})
20+
21+
const onSubmit = async (params: any): Promise<void> => {
22+
const res = await fetch('/api/organization', {
23+
method: 'POST',
24+
headers: {
25+
'Content-Type': 'application/json'
26+
},
27+
body: JSON.stringify({
28+
name: params.name,
29+
creatorId: user.userProfile.id
30+
})
31+
})
32+
if (res.status === 200) {
33+
const data = await res.json()
34+
setOrg(data.organization)
35+
setOrgMembers([user.userProfile])
36+
} else {
37+
const json = await res.json()
38+
setError(json.message)
39+
}
40+
}
41+
42+
return <form
43+
onSubmit={(e) => {
44+
void handleSubmit(onSubmit)(e)
45+
}}
46+
method="post"
47+
>
48+
<div className={style.create_input_ctn}>
49+
<input
50+
{...register('name')}
51+
type="text"
52+
placeholder="Enter the name for your organization."
53+
required
54+
className={style.text_input}
55+
/>
56+
<button className={style.add_btn} onClick={() => (false)}>
57+
Create
58+
</button>
59+
</div>
60+
</form>
61+
}
62+
63+
export default CreateOrganization
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Organization } from '@prisma/client'
2+
import { UserWithSupertokens } from 'services/userService'
3+
import style from './organization.module.css'
4+
5+
interface IProps {
6+
user: UserWithSupertokens
7+
setError: Function
8+
setOrg: Function
9+
org: Omit<Organization, 'createdAt' | 'updatedAt'>
10+
setOrgEdit: Function
11+
}
12+
13+
const DeleteOrganization = ({ user, setError, setOrg, org, setOrgEdit }: IProps): JSX.Element => {
14+
const onDelete = async (): Promise<void> => {
15+
if (org !== null) {
16+
const res = await fetch(`/api/organization?organizationId=${org.id}`, {
17+
method: 'DELETE'
18+
})
19+
20+
if (res.status === 200) {
21+
setOrg(null)
22+
setError('')
23+
setOrgEdit('')
24+
} else {
25+
const json = await res.json()
26+
setError(json.message)
27+
}
28+
}
29+
}
30+
31+
return (<>
32+
<div className={style.confirm_delete_ctn}>
33+
<p>Are you sure you want to delete your organization?<br />This action cannot be undone.</p>
34+
<div className={style.confirm_delete_btn_ctn}>
35+
<button className={style.delete_btn} onClick={() => { void onDelete() }}>
36+
Yes, Delete Organization
37+
</button>
38+
<button className={style.cancel_btn} onClick={() => setOrgEdit('')}>
39+
Cancel
40+
</button>
41+
</div>
42+
</div>
43+
</>
44+
)
45+
}
46+
47+
export default DeleteOrganization
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useEffect, useState } from 'react'
2+
import style from './organization.module.css'
3+
import {
4+
copyTextToClipboard
5+
} from 'utils/index'
6+
7+
const InviteLink = (): JSX.Element => {
8+
const [url, setUrl] = useState('')
9+
const [error, setError] = useState('')
10+
const [copySuccess, setCopySuccess] = useState('')
11+
12+
const elementId = 'inviteLink'
13+
14+
useEffect(() => {
15+
void (async () => {
16+
const res = await fetch('/api/organization/invite')
17+
if (res.status === 200) {
18+
const data = await res.json()
19+
setUrl(data.url)
20+
} else {
21+
const json = await res.json()
22+
setError(json.message)
23+
}
24+
})()
25+
}, [])
26+
27+
return <>
28+
{error !== '' && <div className={style.error_message}>{error}</div>}
29+
<div className={style.invite_link_outer}>
30+
<div id={elementId} className={style.invite_link} onClick={() => copyTextToClipboard(elementId, setCopySuccess)}>
31+
{url === ''
32+
? <p> loading </p>
33+
: <>
34+
<b>{url}{copySuccess === elementId && <div className={style.copied_text}>Copied!</div>}</b>
35+
36+
</>
37+
}
38+
</div>
39+
</div>
40+
41+
</>
42+
}
43+
44+
export default InviteLink
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Organization } from '@prisma/client'
2+
import { UserWithSupertokens } from 'services/userService'
3+
import style from './organization.module.css'
4+
5+
interface IProps {
6+
user: UserWithSupertokens
7+
setError: Function
8+
setOrg: Function
9+
org: Omit<Organization, 'createdAt' | 'updatedAt'>
10+
setOrgEdit: Function
11+
}
12+
13+
const LeaveOrganization = ({ setError, setOrg, org, setOrgEdit }: IProps): JSX.Element => {
14+
const onLeave = async (): Promise<void> => {
15+
if (org !== null) {
16+
const res = await fetch('/api/organization/leave', {
17+
method: 'POST'
18+
})
19+
20+
if (res.status === 200) {
21+
setOrg(null)
22+
setError('')
23+
setOrgEdit('')
24+
} else {
25+
const json = await res.json()
26+
setError(json.message)
27+
}
28+
}
29+
}
30+
31+
return (<>
32+
<div className={style.confirm_delete_ctn} style={{ marginTop: '40px' }}>
33+
<p>Are you sure you want to leave your organization?<br />This action cannot be undone.</p>
34+
<div className={style.confirm_delete_btn_ctn}>
35+
<button className={style.delete_btn} onClick={() => { void onLeave() }}>
36+
Yes, Leave Organization
37+
</button>
38+
<button className={style.cancel_btn} onClick={() => setOrgEdit('')}>
39+
Cancel
40+
</button>
41+
</div>
42+
</div>
43+
</>
44+
)
45+
}
46+
47+
export default LeaveOrganization
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useForm } from 'react-hook-form'
2+
import { UserWithSupertokens } from 'services/userService'
3+
import style from './organization.module.css'
4+
5+
interface IProps {
6+
user: UserWithSupertokens
7+
setError: Function
8+
setOrg: Function
9+
setOrgEdit: Function
10+
}
11+
12+
interface UpdateOrganizationForm {
13+
name: string
14+
}
15+
16+
const UpdateOrganization = ({ user, setError, setOrg, setOrgEdit }: IProps): JSX.Element => {
17+
const { register, handleSubmit, reset } = useForm<UpdateOrganizationForm>({})
18+
19+
const onSubmit = async (params: any): Promise<void> => {
20+
const res = await fetch('/api/organization', {
21+
method: 'PUT',
22+
headers: {
23+
'Content-Type': 'application/json'
24+
},
25+
body: JSON.stringify({
26+
name: params.name,
27+
userId: user.userProfile.id
28+
})
29+
})
30+
if (res.status === 200) {
31+
const data = await res.json()
32+
setOrg(data.organization)
33+
reset()
34+
setOrgEdit('')
35+
} else {
36+
const json = await res.json()
37+
setError(json.message)
38+
}
39+
}
40+
41+
return (
42+
<form
43+
onSubmit={(e) => {
44+
void handleSubmit(onSubmit)(e)
45+
}}
46+
method="post"
47+
>
48+
<label className={style.label}>Change name</label>
49+
<div className={style.create_input_ctn}>
50+
<input
51+
{...register('name')}
52+
type="text"
53+
placeholder="Enter the new name for your organization."
54+
required
55+
className={style.text_input}
56+
/>
57+
<button className={style.add_btn} onClick={() => (false)}>
58+
Update
59+
</button>
60+
</div>
61+
<button className={style.cancel_btn} onClick={() => setOrgEdit('')}>
62+
Cancel
63+
</button>
64+
</form>
65+
)
66+
}
67+
68+
export default UpdateOrganization

0 commit comments

Comments
 (0)