Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
rodrimaia merged 22 commits intomainfromadd_license_settings
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
22 commits
Select commitHold shift + click to select a range
d1e0377
wip: license page
rodrimaiaApr 14, 2023
16e0a5c
WIP
rodrimaiaApr 17, 2023
432456f
WIP
rodrimaiaApr 17, 2023
a26bdc1
wip
rodrimaiaApr 17, 2023
496f535
wip
rodrimaiaApr 18, 2023
bcb5d9a
wip
rodrimaiaApr 18, 2023
7adf2f1
wip
rodrimaiaApr 19, 2023
8ece196
wip
rodrimaiaApr 19, 2023
3774944
wip
rodrimaiaApr 19, 2023
5b782ce
Apply suggestions from code review
rodrimaiaApr 21, 2023
eaba405
wip: ui improvements
rodrimaiaApr 21, 2023
1d107d8
Merge branch 'main' into add_license_settings
rodrimaiaApr 21, 2023
ee583e1
wip: extract components
rodrimaiaApr 24, 2023
21a610e
wip: stories
rodrimaiaApr 24, 2023
fba262c
wip: stories
rodrimaiaApr 24, 2023
0922b5b
fixes from PR reviews
rodrimaiaApr 26, 2023
6ab9d3e
fix stories
rodrimaiaApr 26, 2023
8d55d98
fix empty license page
rodrimaiaApr 26, 2023
832f599
fix copy
rodrimaiaApr 26, 2023
c3a1141
fix
rodrimaiaApr 26, 2023
16a8445
wip
rodrimaiaApr 26, 2023
0322172
add golang test
rodrimaiaApr 26, 2023
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
7 changes: 7 additions & 0 deletionsenterprise/coderd/license/license.go
View file
Open in desktop
Original file line numberDiff line numberDiff 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
9 changes: 9 additions & 0 deletionsenterprise/coderd/license/license_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,6 +37,15 @@ func TestEntitlements(t *testing.T) {
require.Equal(t, codersdk.EntitlementNotEntitled, entitlements.Features[featureName].Entitlement)
}
})
t.Run("Always return the current user count", func(t *testing.T) {
t.Parallel()
db := dbfake.New()
entitlements, err := license.Entitlements(context.Background(), db, slog.Logger{}, 1, 1, coderdenttest.Keys, all)
require.NoError(t, err)
require.False(t, entitlements.HasLicense)
require.False(t, entitlements.Trial)
require.Equal(t, *entitlements.Features[codersdk.FeatureUserLimit].Actual, int64(0))
})
t.Run("SingleLicenseNothing", func(t *testing.T) {
t.Parallel()
db := dbfake.New()
Expand Down
2 changes: 2 additions & 0 deletionssite/package.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,13 +68,15 @@
"react": "18.2.0",
"react-chartjs-2": "4.3.1",
"react-color": "2.19.3",
"react-confetti": "^6.1.0",
"react-dom": "18.2.0",
"react-headless-tabs": "6.0.3",
"react-helmet-async": "1.3.0",
"react-i18next": "12.1.1",
"react-markdown": "8.0.3",
"react-router-dom": "6.4.1",
"react-syntax-highlighter": "15.5.0",
"react-use": "^17.4.0",
"react-virtualized-auto-sizer": "1.0.7",
"react-window": "1.8.8",
"remark-gfm": "3.0.1",
Expand Down
13 changes: 13 additions & 0 deletionssite/src/AppRouter.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -156,6 +156,17 @@ const TemplateSchedulePage = lazy(
),
)

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

export const AppRouter: FC = () => {
return (
<Suspense fallback={<FullScreenLoader />}>
Expand DownExpand Up@@ -244,6 +255,8 @@ export const AppRouter: FC = () => {
element={<DeploySettingsLayout />}
>
<Route path="general" element={<GeneralSettingsPage />} />
<Route path="licenses" element={<LicensesSettingsPage />} />
<Route path="licenses/add" element={<AddNewLicensePage />} />
<Route path="security" element={<SecuritySettingsPage />} />
<Route path="appearance" element={<AppearanceSettingsPage />} />
<Route path="network" element={<NetworkSettingsPage />} />
Expand Down
31 changes: 31 additions & 0 deletionssite/src/api/api.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -965,6 +965,37 @@ 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
}

export 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`)
return response.data
}

export const createLicense = async (
data: TypesGen.AddLicenseRequest,
): Promise<TypesGen.AddLicenseRequest> => {
const response = await axios.post(`/api/v2/licenses`, data)
return response.data
}

export const removeLicense = async (licenseId: number): Promise<void> => {
await axios.delete(`/api/v2/licenses/${licenseId}`)
}

export class MissingBuildParameters extends Error {
parameters: TypesGen.TemplateVersionParameter[] = []
Expand Down
7 changes: 7 additions & 0 deletionssite/src/components/DeploySettingsLayout/Sidebar.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import { makeStyles } from "@material-ui/core/styles"
import Brush from "@material-ui/icons/Brush"
import LaunchOutlined from "@material-ui/icons/LaunchOutlined"
import ApprovalIcon from "@material-ui/icons/VerifiedUserOutlined"
import LockRounded from "@material-ui/icons/LockOutlined"
import Globe from "@material-ui/icons/PublicOutlined"
import VpnKeyOutlined from "@material-ui/icons/VpnKeyOutlined"
Expand DownExpand Up@@ -48,6 +49,12 @@ export const Sidebar: React.FC = () => {
>
General
</SidebarNavItem>
<SidebarNavItem
href="licenses"
icon={<SidebarNavItemIcon icon={ApprovalIcon} />}
>
Licenses
</SidebarNavItem>
<SidebarNavItem
href="appearance"
icon={<SidebarNavItemIcon icon={Brush} />}
Expand Down
178 changes: 178 additions & 0 deletionssite/src/components/FileUpload/FileUpload.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
import { makeStyles } from "@material-ui/core/styles"
import { Stack } from "components/Stack/Stack"
import { FC, DragEvent, useRef, ReactNode } from "react"
import UploadIcon from "@material-ui/icons/CloudUploadOutlined"
import { useClickable } from "hooks/useClickable"
import CircularProgress from "@material-ui/core/CircularProgress"
import { combineClasses } from "utils/combineClasses"
import IconButton from "@material-ui/core/IconButton"
import RemoveIcon from "@material-ui/icons/DeleteOutline"
import FileIcon from "@material-ui/icons/FolderOutlined"

const useFileDrop = (
callback: (file: File) => void,
fileTypeRequired?: string,
): {
onDragOver: (e: DragEvent<HTMLDivElement>) => void
onDrop: (e: DragEvent<HTMLDivElement>) => void
} => {
const onDragOver = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault()
}

const onDrop = (e: DragEvent<HTMLDivElement>) => {
e.preventDefault()
const file = e.dataTransfer.files[0]
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- file can be undefined
if (!file) {
return
}
if (fileTypeRequired && file.type !== fileTypeRequired) {
return
}
callback(file)
}

return {
onDragOver,
onDrop,
}
}

export interface FileUploadProps {
isUploading: boolean
onUpload: (file: File) => void
onRemove?: () => void
file?: File
removeLabel: string
title: string
description?: ReactNode
extension?: string
fileTypeRequired?: string
}

export const FileUpload: FC<FileUploadProps> = ({
isUploading,
onUpload,
onRemove,
file,
removeLabel,
title,
description,
extension,
fileTypeRequired,
}) => {
const styles = useStyles()
const inputRef = useRef<HTMLInputElement>(null)
const tarDrop = useFileDrop(onUpload, fileTypeRequired)
const clickable = useClickable(() => {
if (inputRef.current) {
inputRef.current.click()
}
})

if (!isUploading && file) {
return (
<Stack
className={styles.file}
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Stack direction="row" alignItems="center">
<FileIcon />
<span>{file.name}</span>
</Stack>

<IconButton title={removeLabel} size="small" onClick={onRemove}>
<RemoveIcon />
</IconButton>
</Stack>
)
}

return (
<>
<div
className={combineClasses({
[styles.root]: true,
[styles.disabled]: isUploading,
})}
{...clickable}
{...tarDrop}
>
<Stack alignItems="center" spacing={1}>
{isUploading ? (
<CircularProgress size={32} />
) : (
<UploadIcon className={styles.icon} />
)}

<Stack alignItems="center" spacing={0.5}>
<span className={styles.title}>{title}</span>
<span className={styles.description}>{description}</span>
</Stack>
</Stack>
</div>

<input
type="file"
ref={inputRef}
className={styles.input}
accept={extension}
onChange={(event) => {
const file = event.currentTarget.files?.[0]
if (file) {
onUpload(file)
}
}}
/>
</>
)
}

const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: theme.shape.borderRadius,
border: `2px dashed ${theme.palette.divider}`,
padding: theme.spacing(6),
cursor: "pointer",

"&:hover": {
backgroundColor: theme.palette.background.paper,
},
},

disabled: {
pointerEvents: "none",
opacity: 0.75,
},

icon: {
fontSize: theme.spacing(8),
},

title: {
fontSize: theme.spacing(2),
},

description: {
color: theme.palette.text.secondary,
textAlign: "center",
maxWidth: theme.spacing(50),
},

input: {
display: "none",
},

file: {
borderRadius: theme.shape.borderRadius,
border: `1px solid ${theme.palette.divider}`,
padding: theme.spacing(2),
background: theme.palette.background.paper,
},
}))
Loading

[8]ページ先頭

©2009-2025 Movatter.jp