Skip to content
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

feature: Configuration variable editor (WIP) #449

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
10 changes: 9 additions & 1 deletion apps/backend/src/api/routes/settings.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Sections,
} from '@gitroom/backend/services/auth/permissions/permissions.service';
import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service';
import { IntegrationTagsService } from '@gitroom/nestjs-libraries/database/prisma/integration.tags/integration.tags.service';
import {AddTeamMemberDto} from "@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto";
import {ApiTags} from "@nestjs/swagger";

Expand All @@ -16,7 +17,8 @@ import {ApiTags} from "@nestjs/swagger";
export class SettingsController {
constructor(
private _starsService: StarsService,
private _organizationService: OrganizationService
private _organizationService: OrganizationService,
private _integrationTagsService: IntegrationTagsService
) {}

@Get('/github')
Expand Down Expand Up @@ -104,6 +106,12 @@ export class SettingsController {
return this._starsService.deleteRepository(org.id, id);
}

@Get('/integration-tags')
@CheckPolicies([AuthorizationActions.Create, Sections.INTEGRATION_TAGS], [AuthorizationActions.Create, Sections.ADMIN])
async getIntegrationTags(@GetOrgFromRequest() org: Organization) {
return this._integrationTagsService.getIntegrationTags(org.id);
}

@Get('/team')
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
async getTeam(@GetOrgFromRequest() org: Organization) {
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/src/components/layout/settings.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { useSWRConfig } from 'swr';
import clsx from 'clsx';
import { TeamsComponent } from '@gitroom/frontend/components/settings/teams.component';
import { IntegretionTags } from '@gitroom/frontend/components/settings/integration-tags.component';
Fixed Show fixed Hide fixed
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { LogoutComponent } from '@gitroom/frontend/components/layout/logout.component';
import { useSearchParams } from 'next/navigation';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';

import { FormProvider, SubmitHandler, useForm } from 'react-hook-form';
Fixed Show fixed Hide fixed
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { useMemo } from 'react';
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { SaveConfigurationVariableDto, SaveConfigurationVariablesDto } from '@gitroom/nestjs-libraries/dtos/settings/configuration-variables.dto.ts';
Fixed Show fixed Hide fixed

const cvars = [
{
key: 'USER_REGISTRATION_ENABLED',
description: 'Enable user registration',
datatype: 'bool',
default: 'true',
val: 'true',
section: ['Functionality'],
},
{
key: 'MARKETPLACE_ENABLED',
description: 'Enable marketplace',
datatype: 'bool',
default: 'true',
val: 'true',
section: ['Functionality'],
},
{
key: 'DISCORD_CLIENT_ID',
description: 'Discord client ID',
datatype: 'string',
default: null,
value: null,
section: ['Providers', 'Discord'],
},
{
key: 'DISCORD_CLIENT_SECRET',
description: 'Discord client secret',
datatype: 'string',
default: null,
value: null,
section: ['Providers', 'Discord'],
},
]

export const ConfigurationVariableEditorComponent = () => {
const resolver = useMemo(() => classValidatorResolver(SaveConfigurationVariableDto), []);

const form = useForm({ resolver, values: { message: '' } });

const { data, mutate } = useFetch(`/configuration-variables/all`);
Fixed Show fixed Hide fixed

const submit: SubmitHandler<SaveConfigurationVariableDto> = async (data) => {
Fixed Show fixed Hide fixed
await fetch(`/configuration-variables/${params.id}`, {
method: 'POST',
body: JSON.stringify(data),
});
mutate();
form.reset();
}

return (
<div>
<h2 className = "text-[22px] mb-[6px]">Configuration Variable Editor</h2>
<p className = "mb-[12px]">This screen is only accessible and editable by super admins, it includes configuration that effects the entire app. </p>

<form onSubmit={form.handleSubmit(submit)}>
<FormProvider {...form}>

{cvars.map((cvar) => (
<div className = "grid grid-cols-2 p-1" key={cvar.key}>
<div>
<label className = "">
<strong className = "font-bold">{cvar.key}</strong>
</label>
<p className = "text-customColor18">{cvar.description}</p>
</div>
<div className="">
{cvar.datatype === 'bool' ? (
<input type = "checkbox" name = "" value = "1"></input>
) : (
<input className="bg-input border border-fifth rounded-[4px] text-inputText flex-grow p-1" name="{...form.register(cvar.key)}" value="?" autocomplete="off"></input>
)}
</div>
</div>
))}

</FormProvider>
</form>
</div>
)
}
4 changes: 4 additions & 0 deletions apps/frontend/src/components/settings/settings.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { GithubComponent } from '@gitroom/frontend/components/settings/github.co
import { useCallback, useEffect } from 'react';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { TeamsComponent } from '@gitroom/frontend/components/settings/teams.component';
import { IntegrationTagsComponent } from '@gitroom/frontend/components/settings/integration-tags.component';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
Expand Down Expand Up @@ -70,6 +71,9 @@ export const SettingsComponent = () => {
{/*</div>*/}
</div>
)}

<IntegrationTagsComponent />

{!!user?.tier?.team_members && <TeamsComponent />}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ConfigurationVariableRepository {
constructor(
private _configurationVariable: PrismaRepository<'configurationVariables'>
) {}

getOrDefault(key: string, defaultValue: string) {
const dbVal = this._configurationVariable.model.configurationVariable.findFirst({
where: {
key,
},
});

if (dbVal) {
return dbVal;
} else {
return defaultValue;
}
}

isSet(key: string) {
return !!this._configurationVariable.model.configurationVariable.findFirst({
where: {
key,
},
});
}

set(key: string, value: string) {
return this._configurationVariable.model.configurationVariable.create({
data: {
key,
value,
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@nestjs/common';

@Injectable()
export class ConfigurationVariableService {
constructor(
private _configurationVariableRepository: ConfigurationVariableRepository,
) {}

getOrDefault(key: string, defaultValue: string) {
return this._configurationVariableRepository.getOrDefault(key, defaultValue);
}

getOrEmpty(key: string) {
return this._configurationVariableRepository.getOrDefault(key, '');
}

isSet(key: string) {
return this._configurationVariableRepository.isSet(key);
}

set(key: string, value: string) {
return this._configurationVariableRepository.set(key, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {IsDefined, IsString, ValidateNested} from 'class-validator';
import {Type} from 'class-transformer';

export class SaveConfigurationVariableDto {
@IsString()
key: string;

@IsString()
val: string;
}

export class SaveConfigurationVariablesDto {
@ValidateNested({each: true})
@Type(() => ConfigurationVariableDto)
configurationVariables: Record<string, ConfigurationVariableDto>;
}
Loading