Skip to content
Merged
16 changes: 16 additions & 0 deletions backend/src/controllers/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
EmailTemplateSize,
FrontendSettings,
Settings,
ProjectSettings,
} from "../entities/settings";
import { User } from "../entities/user";
import { UserRole } from "../entities/user-role";
Expand Down Expand Up @@ -91,6 +92,17 @@ export class ApplicationSettingsDTO implements DTO<ApplicationSettings> {
@IsNumber()
@Expose()
public hoursToConfirm!: number;
Comment thread
sezanzeb marked this conversation as resolved.
@Type(() => Date)
@IsDate()
@Expose()
public acceptanceDeadline!: Date;
@Type(() => Date)
@IsDate()
@Expose()
public confirmSpotUntil!: Date;
}

export class ProjectSettingsDTO implements DTO<ProjectSettings> {
@IsBoolean()
@Expose()
public allowRatingProjects!: boolean;
Expand Down Expand Up @@ -166,6 +178,10 @@ export class SettingsDTO implements DTO<Omit<Settings, "updatedAt">> {
@ValidateNested()
@Expose()
public email!: EmailSettingsDTO;
@Type(() => ProjectSettingsDTO)
@ValidateNested()
@Expose()
public project!: ProjectSettingsDTO;
}

export abstract class QuestionConfigurationDTOBase {
Expand Down
9 changes: 7 additions & 2 deletions backend/src/entities/application-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { FormSettings } from "./form-settings";
// TODO all other settings are part of the settings table, whereas ApplicationSettings
// is a separate table. Move into settings table just like EmailSettings.

/**
* Application as in "Peoples application for the event"
*/
@Entity()
export class ApplicationSettings {
@PrimaryGeneratedColumn()
Expand All @@ -29,6 +32,8 @@ export class ApplicationSettings {
public allowProfileFormUntil!: Date;
@Column()
public hoursToConfirm!: number;
@Column({ default: false })
public allowRatingProjects!: boolean;
@Column({ default: () => "CURRENT_TIMESTAMP" })
public acceptanceDeadline!: Date;
@Column({ default: () => "CURRENT_TIMESTAMP" })
public confirmSpotUntil!: Date;
}
8 changes: 8 additions & 0 deletions backend/src/entities/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export class EmailSettings {
public forgotPasswordEmail!: EmailTemplate;
}

export class ProjectSettings {
@Column({ default: false })
public allowRatingProjects!: boolean;
}

@Entity()
export class Settings {
@PrimaryGeneratedColumn()
Expand All @@ -64,6 +69,9 @@ export class Settings {
@Type(() => EmailSettings)
@Column(() => EmailSettings)
public email!: EmailSettings;
@Type(() => ProjectSettings)
@Column(() => ProjectSettings)
public project!: ProjectSettings;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion backend/src/services/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class ProjectService implements IProjectService {
.map((team) => team.id);

const [settings] = await this._settings.find();
const allowRatingProjects = settings.application.allowRatingProjects;
const allowRatingProjects = settings.project.allowRatingProjects;
Comment thread
sezanzeb marked this conversation as resolved.
const isAdmin = user.role === UserRole.Root;

const projects = await this._projects.find();
Expand Down
6 changes: 2 additions & 4 deletions backend/src/services/rating-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,8 @@ export class RatingService implements IRatingService {
}

const settings = await this._settings.getSettings();
if (!settings.application.allowRatingProjects) {
throw new ForbiddenError(
"Rating is not allowed due to application settings",
);
if (!settings.project.allowRatingProjects) {
Comment thread
sezanzeb marked this conversation as resolved.
Comment thread
sezanzeb marked this conversation as resolved.
throw new ForbiddenError("Rating is not allowed due to settings");
}

const project = await this._projects.findOneBy({ id: rating.project.id });
Expand Down
14 changes: 13 additions & 1 deletion backend/src/services/settings-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
EmailTemplate,
FrontendSettings,
Settings,
ProjectSettings,
} from "../entities/settings";
import {
ConfigurationServiceToken,
Expand Down Expand Up @@ -102,6 +103,7 @@ export class SettingsService implements ISettingsService {
settings.application = this.getDefaultApplicationSettings();
settings.frontend = this.getDefaultFrontendSettings();
settings.email = this.getDefaultEmailSettings();
settings.project = this.getDefaultProjectSettings();
return settings;
}

Expand All @@ -115,10 +117,20 @@ export class SettingsService implements ISettingsService {
applicationSettings.allowProfileFormFrom = new Date();
applicationSettings.allowProfileFormUntil = new Date();
applicationSettings.hoursToConfirm = 24;
applicationSettings.allowRatingProjects = false;
applicationSettings.acceptanceDeadline = new Date();
applicationSettings.confirmSpotUntil = new Date();
return applicationSettings;
}

/**
* Creates a project settings object with default values.
*/
private getDefaultProjectSettings(): ProjectSettings {
const projectSettings = new ProjectSettings();
projectSettings.allowRatingProjects = false;
return projectSettings;
}
Comment thread
sezanzeb marked this conversation as resolved.
Comment thread
sezanzeb marked this conversation as resolved.

/**
* Creates a form settings object with default values.
*/
Expand Down
4 changes: 4 additions & 0 deletions backend/test/services/mock/mock-settings-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ export const defaultSettings = {
},
allowProfileFormFrom: new Date(),
allowProfileFormUntil: new Date(),
acceptanceDeadline: new Date(),
confirmSpotUntil: new Date(),
hoursToConfirm: 24,
},
project: {
allowRatingProjects: false,
},
frontend: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ describe(ProjectService.name, () => {
await settingsRepo.save([
{
...defaultSettings,
application: {
...defaultSettings.application,
project: {
...defaultSettings.project,
allowRatingProjects: false,
},
},
Expand All @@ -71,7 +71,7 @@ describe(ProjectService.name, () => {
const allowRatingProjects = async (value: boolean): Promise<void> => {
const settingsRepo = database.getRepository(Settings);
const settings = {
application: {
project: {
allowRatingProjects: value,
},
};
Expand Down
12 changes: 6 additions & 6 deletions backend/test/services/rating-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe("RatingService", () => {
expect.assertions(1);

settingsService.mocks.getSettings.mockResolvedValue({
application: { allowRatingProjects: false },
project: { allowRatingProjects: false },
} as any);

const rating = Object.assign(new Rating(), {
Expand All @@ -140,7 +140,7 @@ describe("RatingService", () => {
expect.assertions(1);

settingsService.mocks.getSettings.mockResolvedValue({
application: { allowRatingProjects: true },
project: { allowRatingProjects: true },
} as any);

const rating = Object.assign(new Rating(), {
Expand All @@ -159,7 +159,7 @@ describe("RatingService", () => {
expect.assertions(1);

settingsService.mocks.getSettings.mockResolvedValue({
application: { allowRatingProjects: true },
project: { allowRatingProjects: true },
} as any);

await projectRepo.update(mockProject.id, { allowRating: false });
Expand All @@ -181,7 +181,7 @@ describe("RatingService", () => {
expect.assertions(1);

settingsService.mocks.getSettings.mockResolvedValue({
application: { allowRatingProjects: true },
project: { allowRatingProjects: true },
} as any);

const rating = Object.assign(new Rating(), {
Expand All @@ -200,7 +200,7 @@ describe("RatingService", () => {
expect.assertions(2);

settingsService.mocks.getSettings.mockResolvedValue({
application: { allowRatingProjects: true },
project: { allowRatingProjects: true },
} as any);

const rating = Object.assign(new Rating(), {
Expand All @@ -220,7 +220,7 @@ describe("RatingService", () => {
expect.assertions(1);

settingsService.mocks.getSettings.mockResolvedValue({
application: { allowRatingProjects: true },
project: { allowRatingProjects: true },
} as any);

const rating = Object.assign(new Rating(), {
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ export class ApiClient {
allowProfileFormUntil: this.reviveDate(
settings.application.allowProfileFormUntil,
),
acceptanceDeadline: this.reviveDate(
settings.application.acceptanceDeadline,
),
confirmSpotUntil: this.reviveDate(
settings.application.confirmSpotUntil,
),
},
};
}
Expand Down
21 changes: 18 additions & 3 deletions frontend/src/components/pages/status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ export const Status = () => {
const { user, updateUser } = useLoginContext();

const confirmationDays = Math.floor(settings.application.hoursToConfirm / 24);

const allowProfileFormFrom = dateToString(
settings.application.allowProfileFormFrom,
);
const allowProfileFormUntil = dateToString(
settings.application.allowProfileFormUntil,
);
const acceptanceDeadline = dateToString(
settings.application.acceptanceDeadline,
);
const confirmSpotUntil = dateToString(settings.application.confirmSpotUntil);

const isExpired = user == null ? false : isConfirmationExpired(user);
const isNotAttending = isExpired || user?.declined;
const deadline = user?.confirmationExpiresAt;
Comment thread
sezanzeb marked this conversation as resolved.
Expand Down Expand Up @@ -98,7 +110,10 @@ export const Status = () => {
<InternalLink to={Routes.ProfileForm}>
profile form
</InternalLink>
, any time between <b>01.03.2026 - 31.04.2026</b>
, any time between{" "}
<b>
{allowProfileFormFrom} - {allowProfileFormUntil}
</b>
</Text>
Comment thread
sezanzeb marked this conversation as resolved.
</>
)}
Expand Down Expand Up @@ -145,7 +160,7 @@ export const Status = () => {
<>
<Text style={{ fontSize: "1.15rem" }}>
We will come back to you and send you a acceptance mail until{" "}
<b>01.05.2026</b>.
<b>{acceptanceDeadline}</b>.
</Text>
</>
)}
Expand Down Expand Up @@ -173,7 +188,7 @@ export const Status = () => {
<>
<Text style={{ fontSize: "1.15rem" }}>
If you got accepted, you need to confirm your spot until{" "}
<b>08.05.2026</b>
<b>{confirmSpotUntil}</b>
{user?.admitted && (
<>
{" "}
Expand Down
Loading
Loading