Skip to content

Commit 0f80beb

Browse files
committed
Add table
1 parent 44bcbde commit 0f80beb

File tree

4 files changed

+193
-6
lines changed

4 files changed

+193
-6
lines changed

site/src/api/api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,10 @@ export const getTemplateDAUs = async (
486486
const response = await axios.get(`/api/v2/templates/${templateId}/daus`)
487487
return response.data
488488
}
489+
490+
export const getTemplateUserRoles = async (
491+
templateId: string,
492+
): Promise<TypesGen.TemplateUser[]> => {
493+
const response = await axios.get(`/api/v2/templates/${templateId}/user-roles`)
494+
return response.data
495+
}

site/src/pages/TemplatePage/TemplateCollaboratorsPage/TemplateCollaboratorsPage.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { useMachine } from "@xstate/react"
12
import { FC } from "react"
23
import { Helmet } from "react-helmet-async"
34
import { useOutletContext } from "react-router-dom"
45
import { pageTitle } from "util/page"
6+
import { templateUsersMachine } from "xServices/template/templateUsersXService"
57
import { TemplateContext } from "xServices/template/templateXService"
68
import { TemplateCollaboratorsPageView } from "./TemplateCollaboratorsPageView"
79

@@ -15,12 +17,18 @@ export const TemplateCollaboratorsPage: FC<React.PropsWithChildren<unknown>> = (
1517
)
1618
}
1719

20+
const [state] = useMachine(templateUsersMachine, { context: { templateId: template.id } })
21+
const { templateUsers } = state.context
22+
1823
return (
1924
<>
2025
<Helmet>
2126
<title>{pageTitle(`${template.name} · Collaborators`)}</title>
2227
</Helmet>
23-
<TemplateCollaboratorsPageView deleteTemplateError={deleteTemplateError} />
28+
<TemplateCollaboratorsPageView
29+
templateUsers={templateUsers}
30+
deleteTemplateError={deleteTemplateError}
31+
/>
2432
</>
2533
)
2634
}
Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,153 @@
1+
import Button from "@material-ui/core/Button"
2+
import CircularProgress from "@material-ui/core/CircularProgress"
3+
import MenuItem from "@material-ui/core/MenuItem"
4+
import Select from "@material-ui/core/Select"
15
import { makeStyles } from "@material-ui/core/styles"
6+
import Table from "@material-ui/core/Table"
7+
import TableBody from "@material-ui/core/TableBody"
8+
import TableCell from "@material-ui/core/TableCell"
9+
import TableContainer from "@material-ui/core/TableContainer"
10+
import TableHead from "@material-ui/core/TableHead"
11+
import TableRow from "@material-ui/core/TableRow"
12+
import TextField from "@material-ui/core/TextField"
13+
import PersonAdd from "@material-ui/icons/PersonAdd"
14+
import Autocomplete from "@material-ui/lab/Autocomplete"
15+
import { TemplateUser } from "api/typesGenerated"
16+
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
17+
import { EmptyState } from "components/EmptyState/EmptyState"
218
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
319
import { Stack } from "components/Stack/Stack"
4-
import { FC } from "react"
20+
import { TableLoader } from "components/TableLoader/TableLoader"
21+
import { FC, useState } from "react"
522

623
export interface TemplateCollaboratorsPageViewProps {
724
deleteTemplateError: Error | unknown
25+
templateUsers: TemplateUser[] | undefined
826
}
927

1028
export const TemplateCollaboratorsPageView: FC<
1129
React.PropsWithChildren<TemplateCollaboratorsPageViewProps>
12-
> = ({ deleteTemplateError }) => {
30+
> = ({ deleteTemplateError, templateUsers }) => {
31+
const styles = useStyles()
32+
const [open, setOpen] = useState(false)
33+
const [options, setOptions] = useState([])
34+
const isLoading = false
1335
const deleteError = deleteTemplateError ? (
1436
<ErrorSummary error={deleteTemplateError} dismissible />
1537
) : null
1638

1739
return (
1840
<Stack spacing={2.5}>
1941
{deleteError}
20-
<h2>Collaborators</h2>
42+
<Stack direction="row" alignItems="center" spacing={1}>
43+
<Autocomplete
44+
id="asynchronous-demo"
45+
style={{ width: 300 }}
46+
open={open}
47+
onOpen={() => {
48+
setOpen(true)
49+
}}
50+
onClose={() => {
51+
setOpen(false)
52+
}}
53+
getOptionSelected={(option: any, value: any) => option.name === value.name}
54+
getOptionLabel={(option) => option.name}
55+
options={options}
56+
loading={isLoading}
57+
className={styles.autocomplete}
58+
renderInput={(params) => (
59+
<TextField
60+
{...params}
61+
margin="none"
62+
variant="outlined"
63+
placeholder="User email or username"
64+
InputProps={{
65+
...params.InputProps,
66+
endAdornment: (
67+
<>
68+
{isLoading ? <CircularProgress size={16} /> : null}
69+
{params.InputProps.endAdornment}
70+
</>
71+
),
72+
}}
73+
/>
74+
)}
75+
/>
76+
77+
<Select defaultValue="read" variant="outlined" className={styles.select}>
78+
<MenuItem key="read" value="read">
79+
Read
80+
</MenuItem>
81+
<MenuItem key="write" value="write">
82+
Write
83+
</MenuItem>
84+
<MenuItem key="admin" value="admin">
85+
Admin
86+
</MenuItem>
87+
</Select>
88+
89+
<Button size="small" startIcon={<PersonAdd />}>
90+
Add collaborator
91+
</Button>
92+
</Stack>
93+
94+
<TableContainer>
95+
<Table>
96+
<TableHead>
97+
<TableRow>
98+
<TableCell>User</TableCell>
99+
<TableCell>Role</TableCell>
100+
</TableRow>
101+
</TableHead>
102+
<TableBody>
103+
<ChooseOne>
104+
<Cond condition={!templateUsers}>
105+
<TableLoader />
106+
</Cond>
107+
<Cond condition={Boolean(templateUsers && templateUsers.length === 0)}>
108+
<TableRow>
109+
<TableCell colSpan={999}>
110+
<EmptyState
111+
message="No collaborators yet"
112+
description="Add a collaborator using the controls above"
113+
/>
114+
</TableCell>
115+
</TableRow>
116+
</Cond>
117+
<Cond condition={Boolean(templateUsers && templateUsers.length > 0)}>
118+
<TableRow>
119+
<TableCell>Kyle</TableCell>
120+
<TableCell>Admin</TableCell>
121+
</TableRow>
122+
</Cond>
123+
</ChooseOne>
124+
</TableBody>
125+
</Table>
126+
</TableContainer>
21127
</Stack>
22128
)
23129
}
24130

25-
export const useStyles = makeStyles(() => {
26-
return {}
131+
export const useStyles = makeStyles((theme) => {
132+
return {
133+
autocomplete: {
134+
"& .MuiInputBase-root": {
135+
width: 300,
136+
// Match button small height
137+
height: 36,
138+
},
139+
140+
"& input": {
141+
fontSize: 14,
142+
padding: `${theme.spacing(0, 0.5, 0, 0.5)} !important`,
143+
},
144+
},
145+
146+
select: {
147+
// Match button small height
148+
height: 36,
149+
fontSize: 14,
150+
width: 100,
151+
},
152+
}
27153
})
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { getTemplateUserRoles } from "api/api"
2+
import { TemplateUser } from "api/typesGenerated"
3+
import { assign, createMachine } from "xstate"
4+
5+
export const templateUsersMachine = createMachine(
6+
{
7+
schema: {
8+
context: {} as {
9+
templateId: string
10+
templateUsers?: TemplateUser[]
11+
},
12+
services: {} as {
13+
loadTemplateUsers: {
14+
data: TemplateUser[]
15+
}
16+
},
17+
},
18+
tsTypes: {} as import("./templateUsersXService.typegen").Typegen0,
19+
id: "templateUserRoles",
20+
initial: "loading",
21+
states: {
22+
loading: {
23+
invoke: {
24+
src: "loadTemplateUsers",
25+
onDone: {
26+
actions: ["assignTemplateUsers"],
27+
target: "success",
28+
},
29+
},
30+
},
31+
success: {
32+
type: "final",
33+
},
34+
},
35+
},
36+
{
37+
services: {
38+
loadTemplateUsers: ({ templateId }) => getTemplateUserRoles(templateId),
39+
},
40+
actions: {
41+
assignTemplateUsers: assign({
42+
templateUsers: (_, { data }) => data,
43+
}),
44+
},
45+
},
46+
)

0 commit comments

Comments
 (0)