Skip to content

Commit

Permalink
Merge branch 'feature/configurable-widgets' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
proohit committed Mar 26, 2022
2 parents 8046055 + 18bd695 commit 16d8237
Show file tree
Hide file tree
Showing 42 changed files with 803 additions and 276 deletions.
30 changes: 6 additions & 24 deletions backend/src/entity/DashboardSettings.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
import { Column, Entity, JoinColumn, ManyToOne, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from './User';

export enum DashboardWidgets {
THIS_MOTH = 'THIS_MONTH',
THIS_YEAR = 'THIS_YEAR',
CURRENT_STATUS = 'CURRENT_STATUS',
LATEST_RECORDS = 'LATEST_RECORDS',
MONTHLY_CATEGORY = 'MONTHLY_CATEGORY',
QUICK_ACTIONS = 'QUICK_ACTIONS',
}

@Entity()
export class DashboardSettings {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({
type: 'simple-array',
enum: DashboardWidgets,
default: [
DashboardWidgets.THIS_MOTH,
DashboardWidgets.THIS_YEAR,
DashboardWidgets.CURRENT_STATUS,
DashboardWidgets.LATEST_RECORDS,
DashboardWidgets.MONTHLY_CATEGORY,
DashboardWidgets.QUICK_ACTIONS,
],
})
enabledWidgets: DashboardWidgets[];
@Column()
widget: string;
@Column()
order: number;
@Column()
ownerUsername: string;
@OneToOne(() => User)
@ManyToOne(() => User)
@JoinColumn({ name: 'ownerUsername' })
owner: User;
}
2 changes: 2 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ app.keys = [config.secret];
app.use(session(app));

import passport from 'koa-passport';
import settingsRouter from './settings/services/settingsRouter';
app.use(passport.initialize());
app.use(passport.session());

Expand Down Expand Up @@ -60,6 +61,7 @@ router.use('/records', recordRouter);
router.use('/wallets', walletRouter);
router.use('/categories', categoryRouter);
router.use('/statistics', statisticsRouter);
router.use('/settings', settingsRouter);

app.use(router.allowedMethods({ throw: true }));
app.use(router.routes());
Expand Down
22 changes: 0 additions & 22 deletions backend/src/migration/1636302462708-AddDashboardSettings.ts

This file was deleted.

28 changes: 28 additions & 0 deletions backend/src/migration/1648214448116-DashboardSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { DEFAULT_WIDGETS } from '../settings/models/Settings';
import { services } from '../shared/services/services';

export class DashboardSettings1648214448116 implements MigrationInterface {
name = 'DashboardSettings1648214448116';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE \`dashboard_settings\` (\`id\` varchar(36) NOT NULL, \`widget\` varchar(255) NOT NULL, \`order\` int NOT NULL, \`ownerUsername\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`,
);
await queryRunner.query(
`ALTER TABLE \`dashboard_settings\` ADD CONSTRAINT \`FK_44eca2a73ba5b456b2a9509e390\` FOREIGN KEY (\`ownerUsername\`) REFERENCES \`user\`(\`username\`) ON DELETE NO ACTION ON UPDATE NO ACTION`,
);

const users = await services.userService.getAllUsers();
for (const user of users) {
await services.settingsService.updateWidgets(user.username, DEFAULT_WIDGETS);
}
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE \`dashboard_settings\` DROP FOREIGN KEY \`FK_44eca2a73ba5b456b2a9509e390\``,
);
await queryRunner.query(`DROP TABLE \`dashboard_settings\``);
}
}
21 changes: 21 additions & 0 deletions backend/src/settings/controllers/SettingsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { services } from '../../shared/services/services';
import { AvailableWidgets, UserSettings } from '../models/Settings';
import { SettingsController } from '../models/SettingsController';

const SETTINGS_CONTROLLER: SettingsController = {
getUserSettings: async (ctx) => {
const settings = await services.settingsService.getUserWidgets(ctx.state.user.username);
const userSettings: UserSettings = { widgets: settings.map((setting) => setting.widget as AvailableWidgets) };
return { data: userSettings, status: 200 };
},
updateSettings: async (ctx) => {
const { widgets } = ctx.request.body;
const updatedSettings = await services.settingsService.updateWidgets(ctx.state.user.username, widgets);
const updatedUserSettings: UserSettings = {
widgets: updatedSettings.map((setting) => setting.widget),
};
return { data: updatedUserSettings, status: 200 };
},
};

export default SETTINGS_CONTROLLER;
28 changes: 28 additions & 0 deletions backend/src/settings/models/Settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const DEFAULT_WIDGETS: AvailableWidgets[] = [
'general-header',
'quick-actions',
'month-picker',
'overview-header',
'month-status',
'category-data',
'current-status',
'daily-records',
'historical-data-header',
'latest-records',
'this-year',
];

export type AvailableWidgets =
| 'general-header'
| 'quick-actions'
| 'month-picker'
| 'overview-header'
| 'month-status'
| 'category-data'
| 'current-status'
| 'daily-records'
| 'historical-data-header'
| 'latest-records'
| 'this-year';

export type UserSettings = { widgets: AvailableWidgets[] };
7 changes: 7 additions & 0 deletions backend/src/settings/models/SettingsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ControllerFunction } from '../../shared/models/BaseController';
import { UserSettings } from './Settings';

export interface SettingsController {
getUserSettings: ControllerFunction<UserSettings>;
updateSettings: ControllerFunction<UserSettings>;
}
18 changes: 18 additions & 0 deletions backend/src/settings/services/SettingsService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { User } from '../../entity/User';
import { repositories } from '../../shared/repositories/database';
import { AvailableWidgets } from '../models/Settings';

export default class SettingsService {
getUserWidgets(username: User['username']) {
return repositories.settings().find({ where: { ownerUsername: username }, order: { order: 'ASC' } });
}
async updateWidgets(username: User['username'], widgets: AvailableWidgets[]) {
const newWidgetOrder = widgets.map((widget, index) => ({
widget,
order: index,
ownerUsername: username,
}));
await repositories.settings().delete({ ownerUsername: username });
return repositories.settings().save(newWidgetOrder);
}
}
9 changes: 9 additions & 0 deletions backend/src/settings/services/settingsRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Router from '@koa/router';
import SETTINGS_CONTROLLER from '../controllers/SettingsController';

const router = new Router();

router.get('/', SETTINGS_CONTROLLER.getUserSettings);
router.put('/', SETTINGS_CONTROLLER.updateSettings);

export default router.routes();
2 changes: 2 additions & 0 deletions backend/src/shared/repositories/authenticationMapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import passport from 'koa-passport';
import { Strategy as LocalStrategy } from 'passport-local';
import { DEFAULT_WIDGETS } from '../../settings/models/Settings';
import { DuplicatedUser } from '../../user/models/Errors';
import { BadRequest, InvalidCredentials, MissingProperty } from '../models/Errors';
import { LoginToken } from '../models/Login';
Expand Down Expand Up @@ -81,5 +82,6 @@ export const register = async (username: string, password: string, email: string
const newUser = await repositories
.users()
.save({ username, password: encryptedPassword, private_key: privateKey, email });
await services.settingsService.updateWidgets(newUser.username, DEFAULT_WIDGETS);
return { username: newUser.username, email: newUser.email };
};
2 changes: 2 additions & 0 deletions backend/src/shared/repositories/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { User } from '../../entity/User';
import { CategoryRepository } from '../../category/repositories/CategoryRepository';
import { WalletRepository } from '../../wallet/repositories/WalletRepository';
import { RecurrentRecord } from '../../entity/RecurrentRecord';
import { DashboardSettings } from '../../entity/DashboardSettings';

export const connection = createConnection();

Expand All @@ -13,4 +14,5 @@ export const repositories = {
categories: () => getCustomRepository(CategoryRepository),
users: () => getRepository(User),
recurrentRecords: () => getRepository(RecurrentRecord),
settings: () => getRepository(DashboardSettings),
};
2 changes: 2 additions & 0 deletions backend/src/shared/services/services.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CategoryService } from '../../category/services/CategoryService';
import { RecordService } from '../../record/services/RecordService';
import { RecurrentRecordService } from '../../record/services/RecurrentRecordService';
import SettingsService from '../../settings/services/SettingsService';
import { StatisticsService } from '../../statistics/services/StatisticsService';
import UserService from '../../user/services/UserService';
import WalletService from '../../wallet/services/WalletService';
Expand All @@ -14,4 +15,5 @@ export const services = {
userService: new UserService(),
statisticsService: new StatisticsService(),
authenticationService: new AuthenticationService(),
settingsService: new SettingsService(),
};
4 changes: 4 additions & 0 deletions backend/src/user/services/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ export default class UserService {
repositories.users().save({ ...user, password: encryptedNewPassword });
return 'Successfully updated password';
}

async getAllUsers() {
return repositories.users().find();
}
}
Loading

0 comments on commit 16d8237

Please sign in to comment.