Skip to content

feat(ui): add system configuration settings page with logo upload#265

Merged
l1ttps merged 7 commits intomainfrom
update-system-config
Jan 31, 2026
Merged

feat(ui): add system configuration settings page with logo upload#265
l1ttps merged 7 commits intomainfrom
update-system-config

Conversation

@l1ttps
Copy link
Copy Markdown
Member

@l1ttps l1ttps commented Jan 31, 2026

No description provided.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @l1ttps, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces comprehensive system configuration management capabilities, allowing administrators to personalize the application's branding by setting a custom system name and uploading or removing a logo. The changes span both the user interface, with a new settings page, and the backend, with new API endpoints and services to handle the configuration data and file storage securely and efficiently.

Highlights

  • New System Configuration Settings Page: A dedicated UI page has been added for managing system-wide configurations, including the system's name and logo.
  • Logo Upload and Removal Functionality: Users with administrative roles can now upload a custom logo for the system, which will be displayed in the UI. The functionality also includes options to preview and remove the uploaded logo.
  • Backend API for System Config and Logo Management: New API endpoints have been introduced to retrieve, update, upload, and remove system configurations and logos, ensuring robust backend support for the UI features.
  • Improved Type Definitions and API Integrations: Updated type definitions across the frontend and backend to accurately reflect the new system configuration and logo properties, along with new query hooks for seamless API interaction.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new system configuration page allowing administrators to update the system name and upload a custom logo. The changes span across the frontend UI, backend API, and database schema. While the new feature is a great addition, I've identified a critical bug in the backend logic that could lead to data loss, where uploading a logo resets the system name. Additionally, I've noticed that some existing tests were removed, which is a significant concern for maintaining code quality and preventing regressions. I've also included a suggestion to refactor some of the new frontend code for better clarity and maintainability. Please address the critical issues before merging.

Comment on lines +45 to 145
mockConfig,
);
(mockSystemConfigRepository.save as jest.Mock).mockResolvedValue({
...mockConfig,
logoPath: null,
});
expect(mockRepository.findOne).toHaveBeenCalledWith({ where: {} });
});

it('should create and return default config if none exists', async () => {
(mockRepository.findOne as jest.Mock).mockResolvedValue(null);
(mockRepository.create as jest.Mock).mockReturnValue(mockConfig);
(mockRepository.save as jest.Mock).mockResolvedValue(mockConfig);

const result = await service.getConfig();
const result = await service.removeLogo();

expect(mockRepository.create).toHaveBeenCalled();
expect(mockRepository.save).toHaveBeenCalled();
expect(mockSystemConfigRepository.findOne).toHaveBeenCalled();
expect(mockSystemConfigRepository.save).toHaveBeenCalledWith({
...mockConfig,
logoPath: null,
});
expect(result).toEqual({
name: mockConfig.name,
logoPath: mockConfig.logoPath,
message: 'System logo removed successfully',
});
});
});

describe('updateConfig', () => {
it('should update config with provided name', async () => {
const updatedConfig = { ...mockConfig, name: 'New Name' };
(mockRepository.findOne as jest.Mock).mockResolvedValue(mockConfig);
(mockRepository.save as jest.Mock).mockResolvedValue(updatedConfig);

const result = await service.updateConfig({ name: 'New Name' });
it('should create default config if none exists and set logoPath to null', async () => {
const mockConfig = {
id: 1,
name: 'OASM',
logoPath: null,
};

expect(mockRepository.save).toHaveBeenCalledWith(
expect.objectContaining({ name: 'New Name' }),
(mockSystemConfigRepository.findOne as jest.Mock).mockResolvedValue(null);
(mockSystemConfigRepository.create as jest.Mock).mockReturnValue(
mockConfig,
);
expect(result.message).toBe('System configuration updated successfully');
});

it('should update config with provided logoPath', async () => {
const updatedConfig = { ...mockConfig, logoPath: '/new-logo.png' };
(mockRepository.findOne as jest.Mock).mockResolvedValue(mockConfig);
(mockRepository.save as jest.Mock).mockResolvedValue(updatedConfig);

const result = await service.updateConfig({ logoPath: '/new-logo.png' });

expect(mockRepository.save).toHaveBeenCalledWith(
expect.objectContaining({ logoPath: '/new-logo.png' }),
(mockSystemConfigRepository.save as jest.Mock).mockResolvedValue(
mockConfig,
);
expect(result.message).toBe('System configuration updated successfully');
});

it('should update config with both name and logoPath', async () => {
const updatedConfig = {
...mockConfig,
name: 'New Name',
logoPath: '/new-logo.png',
};
(mockRepository.findOne as jest.Mock).mockResolvedValue(mockConfig);
(mockRepository.save as jest.Mock).mockResolvedValue(updatedConfig);
const result = await service.removeLogo();

const result = await service.updateConfig({
name: 'New Name',
logoPath: '/new-logo.png',
expect(mockSystemConfigRepository.findOne).toHaveBeenCalled();
expect(mockSystemConfigRepository.create).toHaveBeenCalledWith({
name: 'OASM',
logoPath: undefined,
});
expect(mockSystemConfigRepository.save).toHaveBeenCalledWith(mockConfig);
expect(result).toEqual({
message: 'System logo removed successfully',
});

expect(mockRepository.save).toHaveBeenCalledWith(
expect.objectContaining({
name: 'New Name',
logoPath: '/new-logo.png',
}),
);
expect(result.message).toBe('System configuration updated successfully');
});

it('should not update fields when dto values are undefined', async () => {
(mockRepository.findOne as jest.Mock).mockResolvedValue(mockConfig);
(mockRepository.save as jest.Mock).mockResolvedValue(mockConfig);

await service.updateConfig({});

expect(mockRepository.save).toHaveBeenCalledWith(mockConfig);
});

it('should create default config if none exists before updating', async () => {
(mockRepository.findOne as jest.Mock)
.mockResolvedValueOnce(null)
.mockResolvedValue(mockConfig);
(mockRepository.create as jest.Mock).mockReturnValue(mockConfig);
(mockRepository.save as jest.Mock).mockResolvedValue(mockConfig);

await service.updateConfig({ name: 'New Name' });

expect(mockRepository.create).toHaveBeenCalled();
expect(mockRepository.save).toHaveBeenCalledTimes(2);
});
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

It appears that existing tests for getConfig and updateConfig have been removed. Removing tests, especially when the logic they cover is being changed, is a dangerous practice that can lead to regressions. The tests for updateConfig would likely have caught the bug introduced in system-configs.service.ts where the system name is unintentionally reset.

Please restore and update these tests to ensure the new logic is fully covered, including edge cases.

dto: UpdateSystemConfigDto,
): Promise<DefaultMessageResponseDto> {
const config = await this.findOrCreateConfig();
config.name = dto.name || SystemConfigsService.DEFAULT_SYSTEM_NAME;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This line introduces a critical bug that can lead to data loss. When updateConfig is called without a name in the DTO (e.g., from storage.controller.ts during a logo upload), dto.name is undefined. This causes the expression dto.name || SystemConfigsService.DEFAULT_SYSTEM_NAME to evaluate to the default system name, resetting any custom name the user has configured.

The previous implementation correctly checked if dto.name was defined before attempting to update it. Please restore that conditional logic to prevent accidental data overwrites.

Suggested change
config.name = dto.name || SystemConfigsService.DEFAULT_SYSTEM_NAME;
if (dto.name !== undefined) {
config.name = dto.name;
}

Comment on lines +213 to +228
} else if (shouldRemoveLogo) {
// If logo should be removed, update the config without logoPath
updateConfigMutation.mutate({
data: {
name: values.name,
// Don't include logoPath to remove it (assuming the API handles this)
},
});
} else {
// If no new logo and no logo removal, just update the config
updateConfigMutation.mutate({
data: {
name: values.name,
},
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic in this onSubmit function can be simplified. The else if (shouldRemoveLogo) block is redundant, as it performs the exact same action as the final else block. This redundancy can make the code harder to read and maintain.

Since the handleRemoveLogo function already performs an immediate API call to remove the logo, the shouldRemoveLogo state is not needed to stage the removal. The form submission should only be concerned with saving the system name and handling a new logo upload.

I suggest removing the else if block and the shouldRemoveLogo state entirely for a cleaner implementation.

    } else {
      // If no new logo is being uploaded, just update the config name.
      // The logo removal is handled by a separate mutation in `handleRemoveLogo`.
      updateConfigMutation.mutate({
        data: {
          name: values.name,
        },
      });
    }

@l1ttps l1ttps merged commit 36a4bbe into main Jan 31, 2026
9 checks passed
@l1ttps l1ttps deleted the update-system-config branch February 1, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants