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

Commit87b7537

Browse files
rodrimaiabpmct
andauthored
feat: add license settings UI (#7210)
* wip: license page* WIP* WIP* wip* wip* wip* wip* wip* wip* Apply suggestions from code reviewCo-authored-by: Ben Potter <ben@coder.com>* wip: ui improvements* wip: extract components* wip: stories* wip: stories* fixes from PR reviews* fix stories* fix empty license page* fix copy* fix* wip* add golang test---------Co-authored-by: Ben Potter <ben@coder.com>
1 parentc3fe251 commit87b7537

File tree

17 files changed

+1082
-163
lines changed

17 files changed

+1082
-163
lines changed

‎enterprise/coderd/license/license.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ func Entitlements(
5353
returnentitlements,xerrors.Errorf("query active user count: %w",err)
5454
}
5555

56+
// always shows active user count regardless of license
57+
entitlements.Features[codersdk.FeatureUserLimit]= codersdk.Feature{
58+
Entitlement:codersdk.EntitlementNotEntitled,
59+
Enabled:enablements[codersdk.FeatureUserLimit],
60+
Actual:&activeUserCount,
61+
}
62+
5663
allFeatures:=false
5764

5865
// Here we loop through licenses to detect enabled features.

‎enterprise/coderd/license/license_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ func TestEntitlements(t *testing.T) {
3737
require.Equal(t,codersdk.EntitlementNotEntitled,entitlements.Features[featureName].Entitlement)
3838
}
3939
})
40+
t.Run("Always return the current user count",func(t*testing.T) {
41+
t.Parallel()
42+
db:=dbfake.New()
43+
entitlements,err:=license.Entitlements(context.Background(),db, slog.Logger{},1,1,coderdenttest.Keys,all)
44+
require.NoError(t,err)
45+
require.False(t,entitlements.HasLicense)
46+
require.False(t,entitlements.Trial)
47+
require.Equal(t,*entitlements.Features[codersdk.FeatureUserLimit].Actual,int64(0))
48+
})
4049
t.Run("SingleLicenseNothing",func(t*testing.T) {
4150
t.Parallel()
4251
db:=dbfake.New()

‎site/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@
6868
"react":"18.2.0",
6969
"react-chartjs-2":"4.3.1",
7070
"react-color":"2.19.3",
71+
"react-confetti":"^6.1.0",
7172
"react-dom":"18.2.0",
7273
"react-headless-tabs":"6.0.3",
7374
"react-helmet-async":"1.3.0",
7475
"react-i18next":"12.1.1",
7576
"react-markdown":"8.0.3",
7677
"react-router-dom":"6.4.1",
7778
"react-syntax-highlighter":"15.5.0",
79+
"react-use":"^17.4.0",
7880
"react-virtualized-auto-sizer":"1.0.7",
7981
"react-window":"1.8.8",
8082
"remark-gfm":"3.0.1",

‎site/src/AppRouter.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,17 @@ const TemplateSchedulePage = lazy(
156156
),
157157
)
158158

159+
constLicensesSettingsPage=lazy(
160+
()=>
161+
import(
162+
"./pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPage"
163+
),
164+
)
165+
constAddNewLicensePage=lazy(
166+
()=>
167+
import("./pages/DeploySettingsPage/LicensesSettingsPage/AddNewLicensePage"),
168+
)
169+
159170
exportconstAppRouter:FC=()=>{
160171
return(
161172
<Suspensefallback={<FullScreenLoader/>}>
@@ -244,6 +255,8 @@ export const AppRouter: FC = () => {
244255
element={<DeploySettingsLayout/>}
245256
>
246257
<Routepath="general"element={<GeneralSettingsPage/>}/>
258+
<Routepath="licenses"element={<LicensesSettingsPage/>}/>
259+
<Routepath="licenses/add"element={<AddNewLicensePage/>}/>
247260
<Routepath="security"element={<SecuritySettingsPage/>}/>
248261
<Routepath="appearance"element={<AppearanceSettingsPage/>}/>
249262
<Routepath="network"element={<NetworkSettingsPage/>}/>

‎site/src/api/api.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,37 @@ export const getWorkspaceBuildParameters = async (
965965
)
966966
returnresponse.data
967967
}
968+
typeClaims={
969+
license_expires?:jwt.NumericDate
970+
account_type?:string
971+
account_id?:string
972+
trial:boolean
973+
all_features:boolean
974+
version:number
975+
features:Record<string,number>
976+
require_telemetry?:boolean
977+
}
978+
979+
exporttypeGetLicensesResponse=Omit<TypesGen.License,"claims">&{
980+
claims:Claims
981+
expires_at:string
982+
}
983+
984+
exportconstgetLicenses=async():Promise<GetLicensesResponse[]>=>{
985+
constresponse=awaitaxios.get(`/api/v2/licenses`)
986+
returnresponse.data
987+
}
988+
989+
exportconstcreateLicense=async(
990+
data:TypesGen.AddLicenseRequest,
991+
):Promise<TypesGen.AddLicenseRequest>=>{
992+
constresponse=awaitaxios.post(`/api/v2/licenses`,data)
993+
returnresponse.data
994+
}
995+
996+
exportconstremoveLicense=async(licenseId:number):Promise<void>=>{
997+
awaitaxios.delete(`/api/v2/licenses/${licenseId}`)
998+
}
968999

9691000
exportclassMissingBuildParametersextendsError{
9701001
parameters:TypesGen.TemplateVersionParameter[]=[]

‎site/src/components/DeploySettingsLayout/Sidebar.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import{makeStyles}from"@material-ui/core/styles"
22
importBrushfrom"@material-ui/icons/Brush"
33
importLaunchOutlinedfrom"@material-ui/icons/LaunchOutlined"
4+
importApprovalIconfrom"@material-ui/icons/VerifiedUserOutlined"
45
importLockRoundedfrom"@material-ui/icons/LockOutlined"
56
importGlobefrom"@material-ui/icons/PublicOutlined"
67
importVpnKeyOutlinedfrom"@material-ui/icons/VpnKeyOutlined"
@@ -48,6 +49,12 @@ export const Sidebar: React.FC = () => {
4849
>
4950
General
5051
</SidebarNavItem>
52+
<SidebarNavItem
53+
href="licenses"
54+
icon={<SidebarNavItemIconicon={ApprovalIcon}/>}
55+
>
56+
Licenses
57+
</SidebarNavItem>
5158
<SidebarNavItem
5259
href="appearance"
5360
icon={<SidebarNavItemIconicon={Brush}/>}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import{makeStyles}from"@material-ui/core/styles"
2+
import{Stack}from"components/Stack/Stack"
3+
import{FC,DragEvent,useRef,ReactNode}from"react"
4+
importUploadIconfrom"@material-ui/icons/CloudUploadOutlined"
5+
import{useClickable}from"hooks/useClickable"
6+
importCircularProgressfrom"@material-ui/core/CircularProgress"
7+
import{combineClasses}from"utils/combineClasses"
8+
importIconButtonfrom"@material-ui/core/IconButton"
9+
importRemoveIconfrom"@material-ui/icons/DeleteOutline"
10+
importFileIconfrom"@material-ui/icons/FolderOutlined"
11+
12+
constuseFileDrop=(
13+
callback:(file:File)=>void,
14+
fileTypeRequired?:string,
15+
):{
16+
onDragOver:(e:DragEvent<HTMLDivElement>)=>void
17+
onDrop:(e:DragEvent<HTMLDivElement>)=>void
18+
}=>{
19+
constonDragOver=(e:DragEvent<HTMLDivElement>)=>{
20+
e.preventDefault()
21+
}
22+
23+
constonDrop=(e:DragEvent<HTMLDivElement>)=>{
24+
e.preventDefault()
25+
constfile=e.dataTransfer.files[0]
26+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- file can be undefined
27+
if(!file){
28+
return
29+
}
30+
if(fileTypeRequired&&file.type!==fileTypeRequired){
31+
return
32+
}
33+
callback(file)
34+
}
35+
36+
return{
37+
onDragOver,
38+
onDrop,
39+
}
40+
}
41+
42+
exportinterfaceFileUploadProps{
43+
isUploading:boolean
44+
onUpload:(file:File)=>void
45+
onRemove?:()=>void
46+
file?:File
47+
removeLabel:string
48+
title:string
49+
description?:ReactNode
50+
extension?:string
51+
fileTypeRequired?:string
52+
}
53+
54+
exportconstFileUpload:FC<FileUploadProps>=({
55+
isUploading,
56+
onUpload,
57+
onRemove,
58+
file,
59+
removeLabel,
60+
title,
61+
description,
62+
extension,
63+
fileTypeRequired,
64+
})=>{
65+
conststyles=useStyles()
66+
constinputRef=useRef<HTMLInputElement>(null)
67+
consttarDrop=useFileDrop(onUpload,fileTypeRequired)
68+
constclickable=useClickable(()=>{
69+
if(inputRef.current){
70+
inputRef.current.click()
71+
}
72+
})
73+
74+
if(!isUploading&&file){
75+
return(
76+
<Stack
77+
className={styles.file}
78+
direction="row"
79+
justifyContent="space-between"
80+
alignItems="center"
81+
>
82+
<Stackdirection="row"alignItems="center">
83+
<FileIcon/>
84+
<span>{file.name}</span>
85+
</Stack>
86+
87+
<IconButtontitle={removeLabel}size="small"onClick={onRemove}>
88+
<RemoveIcon/>
89+
</IconButton>
90+
</Stack>
91+
)
92+
}
93+
94+
return(
95+
<>
96+
<div
97+
className={combineClasses({
98+
[styles.root]:true,
99+
[styles.disabled]:isUploading,
100+
})}
101+
{...clickable}
102+
{...tarDrop}
103+
>
104+
<StackalignItems="center"spacing={1}>
105+
{isUploading ?(
106+
<CircularProgresssize={32}/>
107+
) :(
108+
<UploadIconclassName={styles.icon}/>
109+
)}
110+
111+
<StackalignItems="center"spacing={0.5}>
112+
<spanclassName={styles.title}>{title}</span>
113+
<spanclassName={styles.description}>{description}</span>
114+
</Stack>
115+
</Stack>
116+
</div>
117+
118+
<input
119+
type="file"
120+
ref={inputRef}
121+
className={styles.input}
122+
accept={extension}
123+
onChange={(event)=>{
124+
constfile=event.currentTarget.files?.[0]
125+
if(file){
126+
onUpload(file)
127+
}
128+
}}
129+
/>
130+
</>
131+
)
132+
}
133+
134+
constuseStyles=makeStyles((theme)=>({
135+
root:{
136+
display:"flex",
137+
alignItems:"center",
138+
justifyContent:"center",
139+
borderRadius:theme.shape.borderRadius,
140+
border:`2px dashed${theme.palette.divider}`,
141+
padding:theme.spacing(6),
142+
cursor:"pointer",
143+
144+
"&:hover":{
145+
backgroundColor:theme.palette.background.paper,
146+
},
147+
},
148+
149+
disabled:{
150+
pointerEvents:"none",
151+
opacity:0.75,
152+
},
153+
154+
icon:{
155+
fontSize:theme.spacing(8),
156+
},
157+
158+
title:{
159+
fontSize:theme.spacing(2),
160+
},
161+
162+
description:{
163+
color:theme.palette.text.secondary,
164+
textAlign:"center",
165+
maxWidth:theme.spacing(50),
166+
},
167+
168+
input:{
169+
display:"none",
170+
},
171+
172+
file:{
173+
borderRadius:theme.shape.borderRadius,
174+
border:`1px solid${theme.palette.divider}`,
175+
padding:theme.spacing(2),
176+
background:theme.palette.background.paper,
177+
},
178+
}))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp