Skip to content

feat: add license settings UI #7210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
wip
  • Loading branch information
rodrimaia committed Apr 19, 2023
commit 7adf2f171826fa5c520be4a445666e52f592ed1b
8 changes: 8 additions & 0 deletions enterprise/coderd/license/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ func Entitlements(
return entitlements, xerrors.Errorf("query active user count: %w", err)
}

// always shows active user count regardless of license
entitlements.Features[codersdk.FeatureUserLimit] = codersdk.Feature{
Entitlement: codersdk.EntitlementNotEntitled,
Enabled: enablements[codersdk.FeatureUserLimit],
Actual: &activeUserCount,
}

allFeatures := false

// Here we loop through licenses to detect enabled features.
Expand Down Expand Up @@ -79,6 +86,7 @@ func Entitlements(
switch featureName {
// User limit has special treatment as our only non-boolean feature.
case codersdk.FeatureUserLimit:
fmt.Println("user limit-------------", featureValue)
limit := featureValue
priorLimit := entitlements.Features[codersdk.FeatureUserLimit]
if priorLimit.Limit != nil && *priorLimit.Limit > limit {
Expand Down
19 changes: 16 additions & 3 deletions site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,18 @@ const TemplateSchedulePage = lazy(
),
)

const LicensesSettingsPage = lazy(() => import("./pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPage"))
const AddNewLicensePageView = lazy(() => import("./pages/DeploySettingsPage/LicensesSettingsPage/AddNewLicensePageView"))
const LicensesSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPage"
),
)
const AddNewLicensePageView = lazy(
() =>
import(
"./pages/DeploySettingsPage/LicensesSettingsPage/AddNewLicensePageView"
),
)

export const AppRouter: FC = () => {
return (
Expand Down Expand Up @@ -248,7 +258,10 @@ export const AppRouter: FC = () => {
>
<Route path="general" element={<GeneralSettingsPage />} />
<Route path="licenses" element={<LicensesSettingsPage />} />
<Route path="licenses/add" element={<AddNewLicensePageView />} />
<Route
path="licenses/add"
element={<AddNewLicensePageView />}
/>
<Route path="security" element={<SecuritySettingsPage />} />
<Route path="appearance" element={<AppearanceSettingsPage />} />
<Route path="network" element={<NetworkSettingsPage />} />
Expand Down
21 changes: 10 additions & 11 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,24 +966,23 @@ export const getWorkspaceBuildParameters = async (
return response.data
}
type Claims = {
license_expires?: jwt.NumericDate;
account_type?: string;
account_id?: string;
trial: boolean;
all_features: boolean;
version: number;
features: Record<string, number>;
require_telemetry?: boolean;
license_expires?: jwt.NumericDate
account_type?: string
account_id?: string
trial: boolean
all_features: boolean
version: number
features: Record<string, number>
require_telemetry?: boolean
}

type GetLicensesResponse = Omit<TypesGen.License, "claims"> & {
claims: Claims
expires_at: string
}

export const getLicenses = async (): Promise<GetLicensesResponse[]> => {
const response = await axios.get(
`/api/v2/licenses`,
)
const response = await axios.get(`/api/v2/licenses`)
return response.data
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AddNewLicensePageView from "./AddNewLicensePageView";
import AddNewLicensePageView from "./AddNewLicensePageView"

export default {
title: "pages/AddNewLicensePageView",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,34 @@

import { makeStyles, useTheme } from "@material-ui/core/styles"
import Button from "@material-ui/core/Button"
import TextField from "@material-ui/core/TextField"
import { makeStyles } from "@material-ui/core/styles"
import CloudUploadOutlined from "@material-ui/icons/CloudUploadOutlined"
import { Fieldset } from "components/DeploySettingsLayout/Fieldset"
import { Header } from "components/DeploySettingsLayout/Header"
import { FormFields, FormSection } from "components/Form/Form"
import { Stack } from "components/Stack/Stack"
import { DropzoneDialog } from "material-ui-dropzone"
import { FC, PropsWithChildren, useState } from "react"
import Confetti from 'react-confetti'
import { NavLink, Link as RouterLink } from "react-router-dom"
import { useToggle } from 'react-use'
import useWindowSize from 'react-use/lib/useWindowSize'
import TextField from "@material-ui/core/TextField"
import PlusOneOutlined from "@material-ui/icons/PlusOneOutlined"
import { CloudUploadOutlined } from "@material-ui/icons"
import { Link as RouterLink, useNavigate } from "react-router-dom"
import { useToggle } from "react-use"

const AddNewLicense: FC = () => {
const styles = useStyles()
const { width, height } = useWindowSize()
const [confettiOn, toggleConfettiOn] = useToggle(false)
const [isDialogOpen, toggleDialogOpen] = useToggle(false)
const [files, setFiles] = useState<File[]>([]);
const theme = useTheme()
const [files, setFiles] = useState<File[]>([])
const navigate = useNavigate()

function handleSave(files: File[]) {
setFiles(files);
console.log(files)
setFiles(files)
toggleDialogOpen()
toggleConfettiOn()
setTimeout(() => {
toggleConfettiOn(false)
}, 2000)
navigate("/settings/deployment/licenses#success=true")
}

return (
<>
<Confetti
width={width}
height={height}
run={confettiOn}
colors={[theme.palette.primary.main, theme.palette.secondary.main]}
/>
<Stack alignItems="baseline" direction="row" justifyContent="space-between">
<Stack
alignItems="baseline"
direction="row"
justifyContent="space-between"
>
<Header
title="Add your license"
description="Add a license to your account to unlock more features."
Expand All @@ -55,54 +42,40 @@ const AddNewLicense: FC = () => {
</Button>
</Stack>

<Stack
spacing={4}
>
<FormSection
title="Upload license file"
description="please upload the license file you received when you purchased your license."
classes={{
root: styles.formSectionRoot
}}
>


<Stack style={{
height: "100%",
}}>
<div>
<Button
startIcon={<CloudUploadOutlined />}
size="large"
variant="contained"
onClick={() => toggleDialogOpen()}>
Upload license file
</Button>
<DropzoneDialog
open={isDialogOpen}
onSave={handleSave}
// acceptedFiles={['image/jpeg', 'image/png', 'image/bmp']}
showPreviews={false}
maxFileSize={1000000}
onClose={() => toggleDialogOpen(false)}
/>
</div>
</Stack>

</FormSection>
<FormFields>
<Stack className={styles.main}>
<Stack alignItems="center">
<Button
className={styles.ctaButton}
startIcon={<CloudUploadOutlined />}
size="large"
onClick={() => toggleDialogOpen()}
>
Upload License File
</Button>
</Stack>
<DropzoneDialog
open={isDialogOpen}
onSave={handleSave}
// acceptedFiles={['image/jpeg', 'image/png', 'image/bmp']}
showPreviews
maxFileSize={1000000}
onClose={() => toggleDialogOpen(false)}
/>

<DividerWithText>or</DividerWithText>
</FormFields>
<DividerWithText>or</DividerWithText>

<Fieldset
title="Paste your license key"
onSubmit={(data: unknown) => {
console.log(data)
}}
>
<TextField placeholder="Paste your license key here" multiline rows={4} fullWidth />

<TextField
placeholder="Paste your license key here"
multiline
rows={4}
fullWidth
/>
</Fieldset>
</Stack>
</>
Expand All @@ -111,8 +84,15 @@ const AddNewLicense: FC = () => {

export default AddNewLicense


const useStyles = makeStyles(theme => ({
const useStyles = makeStyles((theme) => ({
main: {
paddingTop: theme.spacing(5),
},
ctaButton: {
backgroundImage: `linear-gradient(90deg, ${theme.palette.secondary.main} 0%, ${theme.palette.secondary.dark} 100%)`,
width: theme.spacing(30),
marginBottom: theme.spacing(4),
},
formSectionRoot: {
alignItems: "center",
},
Expand All @@ -123,15 +103,15 @@ const useStyles = makeStyles(theme => ({
title: {
...theme.typography.h5,
fontWeight: 600,
marging: theme.spacing(1)
marging: theme.spacing(1),
},
container: {
display: "flex",
alignItems: "center"
alignItems: "center",
},
border: {
borderBottom: `2px solid ${theme.palette.divider}`,
width: "100%"
width: "100%",
},
content: {
paddingTop: theme.spacing(0.5),
Expand All @@ -140,17 +120,17 @@ const useStyles = makeStyles(theme => ({
paddingLeft: theme.spacing(2),
fontWeight: 500,
fontSize: theme.typography.h5.fontSize,
color: theme.palette.text.secondary
}
}));
color: theme.palette.text.secondary,
},
}))

const DividerWithText: FC<PropsWithChildren> = ({ children }) => {
const classes = useStyles();
const classes = useStyles()
return (
<div className={classes.container}>
<div className={classes.border} />
<span className={classes.content}>{children}</span>
<div className={classes.border} />
</div>
);
};
)
}
Loading