Skip to content

Commit 8ece196

Browse files
committed
wip
1 parent 7adf2f1 commit 8ece196

File tree

3 files changed

+114
-19
lines changed

3 files changed

+114
-19
lines changed

site/src/api/api.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,17 @@ export const getLicenses = async (): Promise<GetLicensesResponse[]> => {
986986
return response.data
987987
}
988988

989+
export const createLicense = async (
990+
data: TypesGen.AddLicenseRequest,
991+
): Promise<TypesGen.AddLicenseRequest> => {
992+
const response = await axios.post(`/api/v2/licenses`, data)
993+
return response.data
994+
}
995+
996+
export const removeLicense = async (licenseId: number): Promise<void> => {
997+
await axios.delete(`/api/v2/licenses/${licenseId}`)
998+
}
999+
9891000
export class MissingBuildParameters extends Error {
9901001
parameters: TypesGen.TemplateVersionParameter[] = []
9911002

site/src/pages/DeploySettingsPage/LicensesSettingsPage/AddNewLicensePageView.tsx

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,54 @@ import Button from "@material-ui/core/Button"
22
import TextField from "@material-ui/core/TextField"
33
import { makeStyles } from "@material-ui/core/styles"
44
import CloudUploadOutlined from "@material-ui/icons/CloudUploadOutlined"
5+
import { useMutation } from "@tanstack/react-query"
6+
import { createLicense } from "api/api"
57
import { Fieldset } from "components/DeploySettingsLayout/Fieldset"
68
import { Header } from "components/DeploySettingsLayout/Header"
9+
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"
710
import { Stack } from "components/Stack/Stack"
811
import { DropzoneDialog } from "material-ui-dropzone"
9-
import { FC, PropsWithChildren, useState } from "react"
12+
import { FC, PropsWithChildren } from "react"
1013
import { Link as RouterLink, useNavigate } from "react-router-dom"
1114
import { useToggle } from "react-use"
1215

1316
const AddNewLicense: FC = () => {
1417
const styles = useStyles()
1518
const [isDialogOpen, toggleDialogOpen] = useToggle(false)
16-
const [files, setFiles] = useState<File[]>([])
1719
const navigate = useNavigate()
1820

19-
function handleSave(files: File[]) {
20-
setFiles(files)
21-
toggleDialogOpen()
22-
navigate("/settings/deployment/licenses#success=true")
21+
const {
22+
mutate: saveLicenseKeyApi,
23+
isLoading: isCreating,
24+
isError: creationFailed,
25+
} = useMutation(createLicense)
26+
27+
function handleFileUploaded(files: File[]) {
28+
const fileReader = new FileReader()
29+
fileReader.onload = () => {
30+
const licenseKey = fileReader.result as string
31+
32+
saveLicenseKey(licenseKey)
33+
34+
fileReader.onerror = () => {
35+
displayError("Failed to read file")
36+
}
37+
}
38+
39+
fileReader.readAsText(files[0])
40+
}
41+
42+
function saveLicenseKey(licenseKey: string) {
43+
saveLicenseKeyApi(
44+
{ license: licenseKey },
45+
{
46+
onSuccess: () => {
47+
displaySuccess("License key saved")
48+
navigate("/settings/deployment/licenses?success=true")
49+
},
50+
onError: () => displayError("Failed to save license key"),
51+
},
52+
)
2353
}
2454

2555
return (
@@ -50,13 +80,12 @@ const AddNewLicense: FC = () => {
5080
size="large"
5181
onClick={() => toggleDialogOpen()}
5282
>
53-
Upload License File
83+
Upload License Key
5484
</Button>
5585
</Stack>
5686
<DropzoneDialog
5787
open={isDialogOpen}
58-
onSave={handleSave}
59-
// acceptedFiles={['image/jpeg', 'image/png', 'image/bmp']}
88+
onSave={handleFileUploaded}
6089
showPreviews
6190
maxFileSize={1000000}
6291
onClose={() => toggleDialogOpen(false)}
@@ -66,11 +95,25 @@ const AddNewLicense: FC = () => {
6695

6796
<Fieldset
6897
title="Paste your license key"
69-
onSubmit={(data: unknown) => {
70-
console.log(data)
98+
validation={creationFailed ? "License key is invalid" : undefined}
99+
onSubmit={(e) => {
100+
e.preventDefault()
101+
102+
const form = e.target
103+
const formData = new FormData(form as HTMLFormElement)
104+
105+
const licenseKey = formData.get("licenseKey")
106+
107+
saveLicenseKey(licenseKey?.toString() || "")
71108
}}
109+
button={
110+
<Button type="submit" disabled={isCreating}>
111+
Save License
112+
</Button>
113+
}
72114
>
73115
<TextField
116+
name="licenseKey"
74117
placeholder="Paste your license key here"
75118
multiline
76119
rows={4}

site/src/pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPage.tsx

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { makeStyles, useTheme } from "@material-ui/core/styles"
22
import RemoveCircleOutlineSharp from "@material-ui/icons/RemoveCircleOutlineSharp"
3-
import { useQuery } from "@tanstack/react-query"
3+
import { useMutation, useQuery } from "@tanstack/react-query"
44
import { useMachine } from "@xstate/react"
5-
import { getLicenses } from "api/api"
5+
import { getLicenses, removeLicense } from "api/api"
66
import { Header } from "components/DeploySettingsLayout/Header"
77
import { Stack } from "components/Stack/Stack"
88
import dayjs from "dayjs"
9-
import { FC, useEffect } from "react"
9+
import { FC, useEffect, useState } from "react"
1010
import { Helmet } from "react-helmet-async"
1111
import { Link, useSearchParams } from "react-router-dom"
1212
import { pageTitle } from "utils/page"
@@ -19,6 +19,8 @@ import Card from "@material-ui/core/Card"
1919
import CardContent from "@material-ui/core/CardContent"
2020
import Box from "@material-ui/core/Box"
2121
import Skeleton from "@material-ui/lab/Skeleton"
22+
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
23+
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"
2224

2325
const LicensesSettingsPage: FC = () => {
2426
const [entitlementsState] = useMachine(entitlementsMachine)
@@ -27,11 +29,21 @@ const LicensesSettingsPage: FC = () => {
2729
const [searchParams, setSearchParams] = useSearchParams()
2830
const success = searchParams.get("success")
2931
const [confettiOn, toggleConfettiOn] = useToggle(false)
32+
const [licenseIDMarkedForRemoval, setLicenseIDMarkedForRemoval] = useState<
33+
number | undefined
34+
>(undefined)
3035
const { width, height } = useWindowSize()
3136

37+
const { mutate: removeLicenseApi, isLoading: isRemovingLicense } =
38+
useMutation(removeLicense)
39+
3240
const theme = useTheme()
3341

34-
const { data: licenses, isLoading } = useQuery({
42+
const {
43+
data: licenses,
44+
isLoading,
45+
refetch: refetchGetLicenses,
46+
} = useQuery({
3547
queryKey: ["licenses"],
3648
queryFn: () => getLicenses(),
3749
})
@@ -49,14 +61,40 @@ const LicensesSettingsPage: FC = () => {
4961
return (
5062
<>
5163
<Helmet>
52-
<title>{pageTitle("General Settings")}</title>
64+
<title>{pageTitle("License Settings")}</title>
5365
</Helmet>
5466
<Confetti
5567
width={width}
5668
height={height}
5769
numberOfPieces={confettiOn ? 200 : 0}
5870
colors={[theme.palette.primary.main, theme.palette.secondary.main]}
5971
/>
72+
<ConfirmDialog
73+
type="info"
74+
hideCancel={false}
75+
open={licenseIDMarkedForRemoval !== undefined}
76+
onConfirm={() => {
77+
if (!licenseIDMarkedForRemoval) {
78+
return
79+
}
80+
removeLicenseApi(licenseIDMarkedForRemoval, {
81+
onSuccess: () => {
82+
displaySuccess("Successfully removed license")
83+
void refetchGetLicenses()
84+
},
85+
onError: () => {
86+
displayError("Failed to remove license")
87+
void refetchGetLicenses()
88+
},
89+
})
90+
setLicenseIDMarkedForRemoval(undefined)
91+
}}
92+
onClose={() => setLicenseIDMarkedForRemoval(undefined)}
93+
title="Confirm removal"
94+
confirmLoading={isRemovingLicense}
95+
confirmText="Remove"
96+
description="Are you sure you want to remove this license?"
97+
/>
6098
<Stack
6199
alignItems="baseline"
62100
direction="row"
@@ -72,7 +110,7 @@ const LicensesSettingsPage: FC = () => {
72110
component={Link}
73111
to="/settings/deployment/licenses/add"
74112
>
75-
Add new License
113+
Add new License key
76114
</Button>
77115
</Stack>
78116

@@ -117,7 +155,9 @@ const LicensesSettingsPage: FC = () => {
117155

118156
<Stack direction="column" spacing={0} alignItems="center">
119157
<span className={styles.expirationDate}>
120-
{dayjs(license.expires_at).format("MMMM D, YYYY")}
158+
{dayjs
159+
.unix(license.claims.license_expires)
160+
.format("MMMM D, YYYY")}
121161
</span>
122162
<span className={styles.expirationDateLabel}>
123163
Valid until
@@ -128,6 +168,7 @@ const LicensesSettingsPage: FC = () => {
128168
startIcon={<RemoveCircleOutlineSharp />}
129169
variant="text"
130170
size="small"
171+
onClick={() => setLicenseIDMarkedForRemoval(license.id)}
131172
>
132173
Remove
133174
</Button>
@@ -143,7 +184,7 @@ const LicensesSettingsPage: FC = () => {
143184
{!isLoading && licenses && licenses.length === 0 && (
144185
<Stack spacing={4} justifyContent="center" alignItems="center">
145186
<Button className={styles.ctaButton} size="large">
146-
Add your license
187+
Add your license key
147188
</Button>
148189
</Stack>
149190
)}

0 commit comments

Comments
 (0)