diff --git a/components/dashboard/src/admin/UserDetail.tsx b/components/dashboard/src/admin/UserDetail.tsx index 2c3c40e1f67fc4..0c91e37d0b9c16 100644 --- a/components/dashboard/src/admin/UserDetail.tsx +++ b/components/dashboard/src/admin/UserDetail.tsx @@ -104,45 +104,52 @@ export default function UserDetail(p: { user: User }) {
{moment(user.creationDate).format('MMM D, YYYY')} setViewAccountStatement(true) - } + }, { + label: 'Grant 20 Extra Hours', + onClick: async () => { + await getGitpodService().server.adminGrantExtraHours(user.id, 20); + setAccountStatement(await getGitpodService().server.adminGetAccountStatement(user.id)); + } + }] } >{accountStatement?.remainingHours ? accountStatement?.remainingHours.toString() : '---'} { - getGitpodService().server.adminSetProfessionalOpenSource(user.id, !isProfessionalOpenSource); + onClick: async () => { + await getGitpodService().server.adminSetProfessionalOpenSource(user.id, !isProfessionalOpenSource); + setAccountStatement(await getGitpodService().server.adminGetAccountStatement(user.id)); } - }} + }]} >{accountStatement?.subscriptions ? accountStatement.subscriptions.filter(s => Subscription.isActive(s, new Date().toISOString())).map(s => Plans.getById(s.planId)?.name).join(', ') : '---'}
{ setEditFeatureFlags(true); } - }} + }]} >{user.featureFlags?.permanentWSFeatureFlags?.join(', ') || '---'} { setEditRoles(true); } - }} + }]} >{user.rolesOrPermissions?.join(', ') || '---'} {isStudent === undefined ? '---' : (isStudent ? 'Enabled' : 'Disabled')}
@@ -185,7 +192,7 @@ function Label(p: { text: string, color: string }) { return
{p.text}
; } -export function Property(p: { name: string, children: string | ReactChild, action?: { label: string, onClick: () => void } }) { +export function Property(p: { name: string, children: string | ReactChild, actions?: { label: string, onClick: () => void }[] }) { return
{p.name} @@ -193,9 +200,11 @@ export function Property(p: { name: string, children: string | ReactChild, actio
{p.children}
-
- {p.action?.label || ''} -
+ {(p.actions || []).map(a => +
+ {a.label || ''} +
+ )}
; } @@ -253,4 +262,4 @@ function getRopEntries(user: User, updateUser: UpdateUserFunction): Entry[] { ...Object.entries(Permissions).map(e => createRopEntry(e[0] as RoleOrPermission)), ...Object.entries(Roles).map(e => createRopEntry(e[0] as RoleOrPermission, true)) ]; -}; \ No newline at end of file +}; diff --git a/components/gitpod-protocol/src/admin-protocol.ts b/components/gitpod-protocol/src/admin-protocol.ts index 16fe3252808dd8..1137a045382989 100644 --- a/components/gitpod-protocol/src/admin-protocol.ts +++ b/components/gitpod-protocol/src/admin-protocol.ts @@ -28,6 +28,7 @@ export interface AdminServer { adminSetProfessionalOpenSource(userId: string, shouldGetProfOSS: boolean): Promise; adminIsStudent(userId: string): Promise; adminAddStudentEmailDomain(userId: string, domain: string): Promise; + adminGrantExtraHours(userId: string, extraHours: number): Promise; } export interface AdminGetListRequest { diff --git a/components/server/src/auth/rate-limiter.ts b/components/server/src/auth/rate-limiter.ts index 2dc0b86a9f9b6f..5ee15c3e6de701 100644 --- a/components/server/src/auth/rate-limiter.ts +++ b/components/server/src/auth/rate-limiter.ts @@ -120,6 +120,7 @@ function readConfig(): RateLimiterConfig { "adminGetAccountStatement": { group: "default", points: 1 }, "adminIsStudent": { group: "default", points: 1 }, "adminSetProfessionalOpenSource": { group: "default", points: 1 }, + "adminGrantExtraHours": { group: "default", points: 1 }, "checkout": { group: "default", points: 1 }, "createPortalSession": { group: "default", points: 1 }, "getAccountStatement": { group: "default", points: 1 }, diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 9e0b0e810b6aef..0f6d03068ce459 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -1643,6 +1643,9 @@ export class GitpodServerImpl { throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`); } + async adminGrantExtraHours(userId: string, extraHours: number): Promise { + throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`); + } async isStudent(): Promise { throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`); }