From 7aff767a4b12f6444525a812078b05c648ca45e7 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 10 Oct 2023 18:50:20 +0200 Subject: [PATCH] Added birthday to wi #401 --- .../src/bot/startup_migration_extension.py | 4 + .../bot_data/migration/birthday_migration.py | 84 +++++++++++++++++++ .../migration/db_history_scripts/users.sql | 37 ++++---- .../migration/fix_user_history_migration.py | 45 ++++++++++ kdb-bot/src/bot_data/model/server_config.py | 14 ++++ kdb-bot/src/bot_data/model/user.py | 18 +++- .../server_config_repository_service.py | 11 +-- .../service/user_repository_service.py | 5 +- .../src/bot_graphql/graphql/serverConfig.gql | 3 + kdb-bot/src/bot_graphql/graphql/user.gql | 2 + .../bot_graphql/mutations/user_mutation.py | 29 +++++-- .../queries/server_config_query.py | 1 + kdb-bot/src/bot_graphql/queries/user_query.py | 1 + kdb-web/package-lock.json | 19 +++-- kdb-web/package.json | 5 +- kdb-web/src/app/app.module.ts | 41 +++++---- kdb-web/src/app/models/data/user.model.ts | 5 +- .../src/app/models/graphql/mutations.model.ts | 7 +- .../src/app/models/graphql/queries.model.ts | 3 + .../src/app/modules/shared/shared.module.ts | 79 +++++++---------- .../server/members/members.component.html | 2 +- .../server/profile/profile.component.html | 48 ++++++++--- .../view/server/profile/profile.component.ts | 44 +++++++--- kdb-web/src/assets/i18n/de.json | 4 + kdb-web/src/assets/version.json | 4 +- kdb-web/src/styles/primeng-fixes.scss | 9 +- .../styles/themes/sh-edraft-dark-theme.scss | 73 ++++++++++++++++ 27 files changed, 449 insertions(+), 148 deletions(-) create mode 100644 kdb-bot/src/bot_data/migration/birthday_migration.py create mode 100644 kdb-bot/src/bot_data/migration/fix_user_history_migration.py diff --git a/kdb-bot/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py index bbf5f328..f697bb9a 100644 --- a/kdb-bot/src/bot/startup_migration_extension.py +++ b/kdb-bot/src/bot/startup_migration_extension.py @@ -9,11 +9,13 @@ from bot_data.migration.api_key_migration import ApiKeyMigration from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_fix1_migration import AutoRoleFix1Migration from bot_data.migration.auto_role_migration import AutoRoleMigration +from bot_data.migration.birthday_migration import BirthdayMigration from bot_data.migration.config_feature_flags_migration import ConfigFeatureFlagsMigration from bot_data.migration.config_migration import ConfigMigration from bot_data.migration.db_history_migration import DBHistoryMigration from bot_data.migration.default_role_migration import DefaultRoleMigration from bot_data.migration.fix_updates_migration import FixUpdatesMigration +from bot_data.migration.fix_user_history_migration import FixUserHistoryMigration from bot_data.migration.initial_migration import InitialMigration from bot_data.migration.level_migration import LevelMigration from bot_data.migration.remove_stats_migration import RemoveStatsMigration @@ -56,3 +58,5 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, ShortRoleNameMigration) # 28.09.2023 #378 - 1.1.7 services.add_transient(MigrationABC, FixUpdatesMigration) # 28.09.2023 #378 - 1.1.7 services.add_transient(MigrationABC, ShortRoleNameOnlyHighestMigration) # 02.10.2023 #391 - 1.1.9 + services.add_transient(MigrationABC, FixUserHistoryMigration) # 10.10.2023 #401 - 1.2.0 + services.add_transient(MigrationABC, BirthdayMigration) # 10.10.2023 #401 - 1.2.0 diff --git a/kdb-bot/src/bot_data/migration/birthday_migration.py b/kdb-bot/src/bot_data/migration/birthday_migration.py new file mode 100644 index 00000000..607de4d3 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/birthday_migration.py @@ -0,0 +1,84 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class BirthdayMigration(MigrationABC): + name = "1.2.0_BirthdayMigration" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + + self._cursor.execute( + str( + f""" + ALTER TABLE Users + ADD Birthday DATE NULL AFTER MessageCount; + """ + ) + ) + + self._cursor.execute( + str( + f""" + ALTER TABLE UsersHistory + ADD Birthday DATE NULL AFTER MessageCount; + """ + ) + ) + self._exec(__file__, "users.sql") + + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_Server + ADD XpForBirthday BIGINT(20) NOT NULL DEFAULT 0 AFTER XpPerAchievement; + """ + ) + ) + + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_ServerHistory + ADD XpForBirthday BIGINT(20) NOT NULL DEFAULT 0 AFTER XpPerAchievement; + """ + ) + ) + self._exec(__file__, "config/server.sql") + + def downgrade(self): + self._cursor.execute( + str( + f""" + ALTER TABLE Users DROP COLUMN Birthday; + """ + ) + ) + self._cursor.execute( + str( + f""" + ALTER TABLE UsersHistory DROP COLUMN Birthday; + """ + ) + ) + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_Server DROP COLUMN XpForBirthday; + """ + ) + ) + self._cursor.execute( + str( + f""" + ALTER TABLE CFG_ServerHistory DROP COLUMN XpForBirthday; + """ + ) + ) diff --git a/kdb-bot/src/bot_data/migration/db_history_scripts/users.sql b/kdb-bot/src/bot_data/migration/db_history_scripts/users.sql index 8dfebf63..a91cf560 100644 --- a/kdb-bot/src/bot_data/migration/db_history_scripts/users.sql +++ b/kdb-bot/src/bot_data/migration/db_history_scripts/users.sql @@ -6,13 +6,16 @@ ALTER TABLE `Users` CREATE TABLE IF NOT EXISTS `UsersHistory` ( - `Id` BIGINT(20) NOT NULL, - `DiscordId` BIGINT(20) NOT NULL, - `XP` BIGINT(20) NOT NULL DEFAULT 0, - `ServerId` BIGINT(20) DEFAULT NULL, - `Deleted` BOOL DEFAULT FALSE, - `DateFrom` DATETIME(6) NOT NULL, - `DateTo` DATETIME(6) NOT NULL + `Id` BIGINT(20) NOT NULL, + `DiscordId` BIGINT(20) NOT NULL, + `XP` BIGINT(20) NOT NULL DEFAULT 0, + `ReactionCount` BIGINT(20) NOT NULL DEFAULT 0, + `MessageCount` BIGINT(20) NOT NULL DEFAULT 0, + `Birthday` DATE NULL, + `ServerId` BIGINT(20) DEFAULT NULL, + `Deleted` BOOL DEFAULT FALSE, + `DateFrom` DATETIME(6) NOT NULL, + `DateTo` DATETIME(6) NOT NULL ); DROP TRIGGER IF EXISTS `TR_UsersUpdate`; @@ -22,12 +25,10 @@ CREATE TRIGGER `TR_UsersUpdate` ON `Users` FOR EACH ROW BEGIN - INSERT INTO `UsersHistory` ( - `Id`, `DiscordId`, `XP`, `ServerId`, `DateFrom`, `DateTo` - ) - VALUES ( - OLD.UserId, OLD.DiscordId, OLD.XP, OLD.ServerId, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6) - ); + INSERT INTO `UsersHistory` (`Id`, `DiscordId`, `XP`, `ReactionCount`, `MessageCount`, `Birthday`, `ServerId`, + `DateFrom`, `DateTo`) + VALUES (OLD.UserId, OLD.DiscordId, OLD.XP, OLD.ReactionCount, OLD.MessageCount, OLD.Birthday, OLD.ServerId, + OLD.LastModifiedAt, CURRENT_TIMESTAMP(6)); END; DROP TRIGGER IF EXISTS `TR_UsersDelete`; @@ -37,10 +38,8 @@ CREATE TRIGGER `TR_UsersDelete` ON `Users` FOR EACH ROW BEGIN - INSERT INTO `UsersHistory` ( - `Id`, `DiscordId`, `XP`, `ServerId`, `Deleted`, `DateFrom`, `DateTo` - ) - VALUES ( - OLD.UserId, OLD.DiscordId, OLD.XP, OLD.ServerId, TRUE, OLD.LastModifiedAt, CURRENT_TIMESTAMP(6) - ); + INSERT INTO `UsersHistory` (`Id`, `DiscordId`, `XP`, `ReactionCount`, `MessageCount`, `Birthday`, `ServerId`, + `Deleted`, `DateFrom`, `DateTo`) + VALUES (OLD.UserId, OLD.DiscordId, OLD.XP, OLD.ReactionCount, OLD.MessageCount, OLD.Birthday, OLD.ServerId, TRUE, + OLD.LastModifiedAt, CURRENT_TIMESTAMP(6)); END; \ No newline at end of file diff --git a/kdb-bot/src/bot_data/migration/fix_user_history_migration.py b/kdb-bot/src/bot_data/migration/fix_user_history_migration.py new file mode 100644 index 00000000..b39ec429 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/fix_user_history_migration.py @@ -0,0 +1,45 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class FixUserHistoryMigration(MigrationABC): + name = "1.2.0_FixUserHistoryMigration" + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, "Running upgrade") + + # fix 1.1.0_AchievementsMigration + self._cursor.execute( + str( + f"""ALTER TABLE UsersHistory ADD COLUMN IF NOT EXISTS ReactionCount BIGINT NOT NULL DEFAULT 0 AFTER XP;""" + ) + ) + self._cursor.execute( + str( + f"""ALTER TABLE UsersHistory ADD COLUMN IF NOT EXISTS MessageCount BIGINT NOT NULL DEFAULT 0 AFTER ReactionCount;""" + ) + ) + self._exec(__file__, "users.sql") + + def downgrade(self): + self._cursor.execute( + str( + f""" + ALTER TABLE UsersHistory DROP COLUMN MessageCount; + """ + ) + ) + self._cursor.execute( + str( + f""" + ALTER TABLE UsersHistory DROP COLUMN ReactionCount; + """ + ) + ) diff --git a/kdb-bot/src/bot_data/model/server_config.py b/kdb-bot/src/bot_data/model/server_config.py index 8a36ac6f..b10410d7 100644 --- a/kdb-bot/src/bot_data/model/server_config.py +++ b/kdb-bot/src/bot_data/model/server_config.py @@ -24,6 +24,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): xp_per_ontime_hour: int, xp_per_event_participation: int, xp_per_achievement: int, + xp_for_birthday: int, afk_command_channel_id: int, help_voice_channel_id: int, team_channel_id: int, @@ -48,6 +49,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): self._xp_per_ontime_hour = xp_per_ontime_hour self._xp_per_event_participation = xp_per_event_participation self._xp_per_achievement = xp_per_achievement + self._xp_for_birthday = xp_for_birthday self._afk_command_channel_id = afk_command_channel_id self._help_voice_channel_id = help_voice_channel_id self._team_channel_id = team_channel_id @@ -76,6 +78,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): 10, 10, 10, + 10, guild.system_channel.id, guild.system_channel.id, guild.system_channel.id, @@ -164,6 +167,14 @@ class ServerConfig(TableABC, ConfigurationModelABC): def xp_per_achievement(self, value: int): self._xp_per_achievement = value + @property + def xp_for_birthday(self) -> int: + return self._xp_for_birthday + + @xp_for_birthday.setter + def xp_for_birthday(self, value: int): + self._xp_for_birthday = value + @property def afk_command_channel_id(self) -> int: return self._afk_command_channel_id @@ -280,6 +291,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): `XpPerOntimeHour`, `XpPerEventParticipation`, `XpPerAchievement`, + `XpForBirthday`, `AFKCommandChannelId`, `HelpVoiceChannelId`, `TeamChannelId`, @@ -298,6 +310,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): {self._xp_per_ontime_hour}, {self._xp_per_event_participation}, {self._xp_per_achievement}, + '{self._xp_for_birthday}', {self._afk_command_channel_id}, {self._help_voice_channel_id}, {self._team_channel_id}, @@ -324,6 +337,7 @@ class ServerConfig(TableABC, ConfigurationModelABC): `XpPerOntimeHour` = {self._xp_per_ontime_hour}, `XpPerEventParticipation` = {self._xp_per_event_participation}, `XpPerAchievement` = {self._xp_per_achievement}, + `XpForBirthday` = {self._xp_for_birthday}, `AFKCommandChannelId` = {self._afk_command_channel_id}, `HelpVoiceChannelId` = {self._help_voice_channel_id}, `TeamChannelId` = {self._team_channel_id}, diff --git a/kdb-bot/src/bot_data/model/user.py b/kdb-bot/src/bot_data/model/user.py index 57e1cc67..310ecc08 100644 --- a/kdb-bot/src/bot_data/model/user.py +++ b/kdb-bot/src/bot_data/model/user.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, date from typing import Optional from cpl_core.database import TableABC @@ -17,6 +17,7 @@ class User(TableABC): xp: int, reaction_count: int, message_count: int, + birthday: Optional[date], server: Optional[Server], created_at: datetime = None, modified_at: datetime = None, @@ -27,6 +28,7 @@ class User(TableABC): self._xp = xp self._reaction_count = reaction_count self._message_count = message_count + self._birthday = birthday self._server = server TableABC.__init__(self) @@ -79,6 +81,14 @@ class User(TableABC): def reaction_count(self, value: int): self._reaction_count = value + @property + def birthday(self) -> Optional[datetime]: + return self._birthday + + @birthday.setter + def birthday(self, value: Optional[datetime]): + self._birthday = value + @property @ServiceProviderABC.inject def ontime(self, services: ServiceProviderABC) -> float: @@ -171,12 +181,13 @@ class User(TableABC): return str( f""" INSERT INTO `Users` ( - `DiscordId`, `XP`, `MessageCount`, `ReactionCount`, `ServerId` + `DiscordId`, `XP`, `MessageCount`, `ReactionCount`, `Birthday`, `ServerId` ) VALUES ( {self._discord_id}, {self._xp}, {self._message_count}, {self._reaction_count}, + '{self._birthday}', {self._server.id} ); """ @@ -189,7 +200,8 @@ class User(TableABC): UPDATE `Users` SET `XP` = {self._xp}, `MessageCount` = {self._message_count}, - `ReactionCount` = {self._reaction_count} + `ReactionCount` = {self._reaction_count}, + `Birthday` = '{self._birthday}' WHERE `UserId` = {self._user_id}; """ ) diff --git a/kdb-bot/src/bot_data/service/server_config_repository_service.py b/kdb-bot/src/bot_data/service/server_config_repository_service.py index 4e5d3dcd..391649a5 100644 --- a/kdb-bot/src/bot_data/service/server_config_repository_service.py +++ b/kdb-bot/src/bot_data/service/server_config_repository_service.py @@ -66,12 +66,13 @@ class ServerConfigRepositoryService(ServerConfigRepositoryABC): result[13], result[14], result[15], - json.loads(result[16]), - self._servers.get_server_by_id(result[17]), - self._get_afk_channel_ids(result[17]), - self._get_team_role_ids(result[17]), - result[18], + result[16], + json.loads(result[17]), + self._servers.get_server_by_id(result[18]), + self._get_afk_channel_ids(result[18]), + self._get_team_role_ids(result[18]), result[19], + result[20], id=result[0], ) diff --git a/kdb-bot/src/bot_data/service/user_repository_service.py b/kdb-bot/src/bot_data/service/user_repository_service.py index 73387908..1238fa1b 100644 --- a/kdb-bot/src/bot_data/service/user_repository_service.py +++ b/kdb-bot/src/bot_data/service/user_repository_service.py @@ -29,9 +29,10 @@ class UserRepositoryService(UserRepositoryABC): result[2], result[3], result[4], - self._servers.get_server_by_id(result[5]), - result[6], + result[5].strftime("%d.%m.%Y") if result[5] is not None else None, + self._servers.get_server_by_id(result[6]), result[7], + result[8], id=result[0], ) diff --git a/kdb-bot/src/bot_graphql/graphql/serverConfig.gql b/kdb-bot/src/bot_graphql/graphql/serverConfig.gql index 7683194c..b2ab507a 100644 --- a/kdb-bot/src/bot_graphql/graphql/serverConfig.gql +++ b/kdb-bot/src/bot_graphql/graphql/serverConfig.gql @@ -9,6 +9,7 @@ type ServerConfig implements TableWithHistoryQuery { xpPerOntimeHour: Int xpPerEventParticipation: Int xpPerAchievement: Int + xpForBirthday: Int afkCommandChannelId: String helpVoiceChannelId: String teamChannelId: String @@ -41,6 +42,7 @@ type ServerConfigHistory implements HistoryTableQuery { xpPerOntimeHour: Int xpPerEventParticipation: Int xpPerAchievement: Int + xpForBirthday: Int afkCommandChannelId: String helpVoiceChannelId: String teamChannelId: String @@ -91,6 +93,7 @@ input ServerConfigInput { xpPerOntimeHour: Int xpPerEventParticipation: Int xpPerAchievement: Int + xpForBirthday: Int afkCommandChannelId: String helpVoiceChannelId: String teamChannelId: String diff --git a/kdb-bot/src/bot_graphql/graphql/user.gql b/kdb-bot/src/bot_graphql/graphql/user.gql index f89e8483..9ffff9a2 100644 --- a/kdb-bot/src/bot_graphql/graphql/user.gql +++ b/kdb-bot/src/bot_graphql/graphql/user.gql @@ -5,6 +5,7 @@ type User implements TableWithHistoryQuery { xp: Int messageCount: Int reactionCount: Int + birthday: String ontime: Float level: Level @@ -62,6 +63,7 @@ type UserMutation { input UserInput { id: ID xp: Int + birthday: String levelId: ID userWarnings: [UserWarningInput] } \ No newline at end of file diff --git a/kdb-bot/src/bot_graphql/mutations/user_mutation.py b/kdb-bot/src/bot_graphql/mutations/user_mutation.py index 6c6c0bc6..9ec5f4cc 100644 --- a/kdb-bot/src/bot_graphql/mutations/user_mutation.py +++ b/kdb-bot/src/bot_graphql/mutations/user_mutation.py @@ -1,6 +1,9 @@ +from datetime import datetime + from cpl_core.database.context import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC +from bot_api.route.route import Route from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC @@ -42,18 +45,26 @@ class UserMutation(QueryABC): def resolve_update_user(self, *_, input: dict): user = self._users.get_user_by_id(input["id"]) - self._can_user_mutate_data(user.server, UserRoleEnum.moderator) - new_xp = None - if "levelId" in input: - level = self._levels.get_level_by_id(input["levelId"]) - if user.level.id != level.id: - new_xp = level.min_xp + auth_user = Route.get_user() + member = self._bot.get_guild(user.server.discord_id).get_member( + auth_user.users.where(lambda x: x.server.id == user.server.id).single().discord_id + ) + if member.id != user.discord_id: + self._can_user_mutate_data(user.server, UserRoleEnum.moderator) - user.xp = new_xp if new_xp is not None else input["xp"] if "xp" in input else user.xp + new_xp = None + if "levelId" in input: + level = self._levels.get_level_by_id(input["levelId"]) + if user.level.id != level.id: + new_xp = level.min_xp - if "userWarnings" in input: - self._update_user_warning(user, input["userWarnings"]) + if "userWarnings" in input: + self._update_user_warning(user, input["userWarnings"]) + + user.xp = new_xp if new_xp is not None else input["xp"] if "xp" in input else user.xp + + user.birthday = datetime.strptime(input["birthday"], "%d.%m.%Y") if "birthday" in input else user.birthday self._users.update_user(user) self._db.save_changes() diff --git a/kdb-bot/src/bot_graphql/queries/server_config_query.py b/kdb-bot/src/bot_graphql/queries/server_config_query.py index 5e580eca..b995927a 100644 --- a/kdb-bot/src/bot_graphql/queries/server_config_query.py +++ b/kdb-bot/src/bot_graphql/queries/server_config_query.py @@ -20,6 +20,7 @@ class ServerConfigQuery(DataQueryWithHistoryABC): self.set_field("xpPerOntimeHour", lambda config, *_: config.xp_per_ontime_hour) self.set_field("xpPerEventParticipation", lambda config, *_: config.xp_per_event_participation) self.set_field("xpPerAchievement", lambda config, *_: config.xp_per_achievement) + self.set_field("xpForBirthday", lambda config, *_: config.xp_for_birthday) self.set_field("afkCommandChannelId", lambda config, *_: config.afk_command_channel_id) self.set_field("helpVoiceChannelId", lambda config, *_: config.help_voice_channel_id) self.set_field("teamChannelId", lambda config, *_: config.team_channel_id) diff --git a/kdb-bot/src/bot_graphql/queries/user_query.py b/kdb-bot/src/bot_graphql/queries/user_query.py index f015c847..c04fbcc9 100644 --- a/kdb-bot/src/bot_graphql/queries/user_query.py +++ b/kdb-bot/src/bot_graphql/queries/user_query.py @@ -50,6 +50,7 @@ class UserQuery(DataQueryWithHistoryABC): self.set_field("xp", self.resolve_xp) self.set_field("messageCount", lambda x, *_: x.message_count) self.set_field("reactionCount", lambda x, *_: x.reaction_count) + self.set_field("birthday", lambda x, *_: x.birthday) self.set_field("ontime", self.resolve_ontime) self.set_field("level", self.resolve_level) self.add_collection( diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json index 9281a434..aa28d9bb 100644 --- a/kdb-web/package-lock.json +++ b/kdb-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "kdb-web", - "version": "1.0.dev127_config_in_wi", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kdb-web", - "version": "1.0.dev127_config_in_wi", + "version": "1.2.0", "dependencies": { "@angular/animations": "^15.1.4", "@angular/common": "^15.1.4", @@ -21,7 +21,7 @@ "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", "@types/socket.io-client": "^3.0.0", - "primeflex": "^3.3.1", + "moment": "^2.29.4", "primeicons": "^6.0.1", "primeng": "^15.2.0", "rxjs": "~7.5.0", @@ -8157,6 +8157,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9303,11 +9311,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/primeflex": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz", - "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==" - }, "node_modules/primeicons": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz", diff --git a/kdb-web/package.json b/kdb-web/package.json index c7f77d8c..064e549b 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -1,6 +1,6 @@ { "name": "kdb-web", - "version": "1.1.dev402", + "version": "1.2.0", "scripts": { "ng": "ng", "update-version": "ts-node update-version.ts", @@ -30,6 +30,7 @@ "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", "@types/socket.io-client": "^3.0.0", + "moment": "^2.29.4", "primeicons": "^6.0.1", "primeng": "^15.2.0", "rxjs": "~7.5.0", @@ -51,4 +52,4 @@ "tslib": "^2.4.1", "typescript": "~4.9.5" } -} \ No newline at end of file +} diff --git a/kdb-web/src/app/app.module.ts b/kdb-web/src/app/app.module.ts index 3e2b348b..e6d9877d 100644 --- a/kdb-web/src/app/app.module.ts +++ b/kdb-web/src/app/app.module.ts @@ -1,23 +1,22 @@ -import { HttpClient, HttpClientModule } from '@angular/common/http'; -import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { JwtModule } from '@auth0/angular-jwt'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { DialogService } from 'primeng/dynamicdialog'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { NotFoundComponent } from './components/error/not-found/not-found.component'; -import { FooterComponent } from './components/footer/footer.component'; -import { HeaderComponent } from './components/header/header.component'; -import { SidebarComponent } from './components/sidebar/sidebar.component'; -import { SpinnerComponent } from './components/spinner/spinner.component'; -import { SharedModule } from './modules/shared/shared.module'; -import { ErrorHandlerService } from './services/error-handler/error-handler.service'; -import { SettingsService } from './services/settings/settings.service'; - +import { HttpClient, HttpClientModule } from "@angular/common/http"; +import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core"; +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { JwtModule } from "@auth0/angular-jwt"; +import { TranslateLoader, TranslateModule } from "@ngx-translate/core"; +import { TranslateHttpLoader } from "@ngx-translate/http-loader"; +import { ConfirmationService, MessageService } from "primeng/api"; +import { DialogService } from "primeng/dynamicdialog"; +import { AppRoutingModule } from "./app-routing.module"; +import { AppComponent } from "./app.component"; +import { NotFoundComponent } from "./components/error/not-found/not-found.component"; +import { FooterComponent } from "./components/footer/footer.component"; +import { HeaderComponent } from "./components/header/header.component"; +import { SidebarComponent } from "./components/sidebar/sidebar.component"; +import { SpinnerComponent } from "./components/spinner/spinner.component"; +import { SharedModule } from "./modules/shared/shared.module"; +import { ErrorHandlerService } from "./services/error-handler/error-handler.service"; +import { SettingsService } from "./services/settings/settings.service"; @NgModule({ @@ -63,7 +62,7 @@ import { SettingsService } from './services/settings/settings.service'; }, MessageService, ConfirmationService, - DialogService + DialogService, ], bootstrap: [AppComponent] }) diff --git a/kdb-web/src/app/models/data/user.model.ts b/kdb-web/src/app/models/data/user.model.ts index 8082e781..5dcd05aa 100644 --- a/kdb-web/src/app/models/data/user.model.ts +++ b/kdb-web/src/app/models/data/user.model.ts @@ -12,8 +12,9 @@ export interface User extends DataWithHistory { discordId?: number; name?: string; xp?: number; - message_count?: number; - reaction_count?: number; + messageCount?: number; + reactionCount?: number; + birthday?: string; ontime?: number; level?: Level; server?: Server; diff --git a/kdb-web/src/app/models/graphql/mutations.model.ts b/kdb-web/src/app/models/graphql/mutations.model.ts index 8a877cea..dda0301c 100644 --- a/kdb-web/src/app/models/graphql/mutations.model.ts +++ b/kdb-web/src/app/models/graphql/mutations.model.ts @@ -1,11 +1,14 @@ export class Mutations { static updateUser = ` - mutation updateUser($id: ID, $xp: Int, $levelId: ID, $userWarnings: [UserWarningInput]) { + mutation updateUser($id: ID, $xp: Int $birthday: String, $levelId: ID, $userWarnings: [UserWarningInput]) { user { - updateUser(input: { id: $id, xp: $xp, levelId: $levelId, userWarnings: $userWarnings }) { + updateUser(input: { id: $id, xp: $xp, birthday: $birthday, levelId: $levelId, userWarnings: $userWarnings }) { id name xp + messageCount + reactionCount + birthday level { id name diff --git a/kdb-web/src/app/models/graphql/queries.model.ts b/kdb-web/src/app/models/graphql/queries.model.ts index 2e9d362b..dbf49e10 100644 --- a/kdb-web/src/app/models/graphql/queries.model.ts +++ b/kdb-web/src/app/models/graphql/queries.model.ts @@ -285,6 +285,9 @@ export class Queries { discordId name xp + messageCount + reactionCount + birthday ontime level { id diff --git a/kdb-web/src/app/modules/shared/shared.module.ts b/kdb-web/src/app/modules/shared/shared.module.ts index 9cea8e7f..f05452c0 100644 --- a/kdb-web/src/app/modules/shared/shared.module.ts +++ b/kdb-web/src/app/modules/shared/shared.module.ts @@ -32,8 +32,37 @@ import { HideableHeaderComponent } from './components/hideable-header/hideable-h import { MultiSelectColumnsComponent } from './base/multi-select-columns/multi-select-columns.component'; import { FeatureFlagListComponent } from './components/feature-flag-list/feature-flag-list.component'; import { InputSwitchModule } from "primeng/inputswitch"; +import { CalendarModule } from "primeng/calendar"; +const PrimeNGModules = [ + ButtonModule, + PasswordModule, + MenuModule, + DialogModule, + ProgressSpinnerModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + ToastModule, + ConfirmDialogModule, + TableModule, + InputTextModule, + CheckboxModule, + DropdownModule, + TranslateModule, + DynamicDialogModule, + PanelMenuModule, + PanelModule, + InputNumberModule, + ImageModule, + SidebarModule, + DataViewModule, + MultiSelectModule, + InputSwitchModule, + CalendarModule, +] + @NgModule({ declarations: [ AuthRolePipe, @@ -48,66 +77,20 @@ import { InputSwitchModule } from "primeng/inputswitch"; ], imports: [ CommonModule, - ButtonModule, - PasswordModule, - MenuModule, - DialogModule, - ProgressSpinnerModule, - HttpClientModule, - FormsModule, - ReactiveFormsModule, - ToastModule, - ConfirmDialogModule, - TableModule, - InputTextModule, - CheckboxModule, - DropdownModule, - TranslateModule, - DynamicDialogModule, - PanelMenuModule, - PanelModule, - InputNumberModule, - ImageModule, - SidebarModule, - DataViewModule, - MultiSelectModule, - InputSwitchModule, + ...PrimeNGModules ], exports: [ - ButtonModule, - PasswordModule, - MenuModule, - DialogModule, - ProgressSpinnerModule, - HttpClientModule, - FormsModule, - ReactiveFormsModule, - ToastModule, - ConfirmDialogModule, - TableModule, - InputTextModule, - CheckboxModule, - DropdownModule, - TranslateModule, - DynamicDialogModule, - PanelMenuModule, - PanelModule, + ...PrimeNGModules, AuthRolePipe, IpAddressPipe, BoolPipe, - InputNumberModule, - ImageModule, - SidebarModule, HistoryBtnComponent, - DataViewModule, DataViewLayoutOptions, ConfigListComponent, - MultiSelectModule, HideableColumnComponent, HideableHeaderComponent, MultiSelectColumnsComponent, FeatureFlagListComponent, - InputSwitchModule, ] }) export class SharedModule { diff --git a/kdb-web/src/app/modules/view/server/members/members.component.html b/kdb-web/src/app/modules/view/server/members/members.component.html index 1054f688..cb4271ed 100644 --- a/kdb-web/src/app/modules/view/server/members/members.component.html +++ b/kdb-web/src/app/modules/view/server/members/members.component.html @@ -204,7 +204,7 @@ {{'common.level' | translate}}: - + {{member.level.name}} diff --git a/kdb-web/src/app/modules/view/server/profile/profile.component.html b/kdb-web/src/app/modules/view/server/profile/profile.component.html index 46ddaf7c..43cca8b0 100644 --- a/kdb-web/src/app/modules/view/server/profile/profile.component.html +++ b/kdb-web/src/app/modules/view/server/profile/profile.component.html @@ -30,7 +30,32 @@
{{'view.server.profile.xp' | translate}}:
-
{{user.xp}}
+
{{user.xp}}
+
+
+
+ +
+
+
{{'view.server.profile.message_count' | translate}}:
+
{{user.messageCount}}
+
+
+ +
+
+
{{'view.server.profile.reaction_count' | translate}}:
+
{{user.reactionCount}}
+
+
+ +
+
+
{{'view.server.profile.birthday' | translate}}:
+
{{user.birthday}}
+
+ +
@@ -41,17 +66,14 @@ - - - - - - -
{{'view.server.profile.level' | translate}}:
-
{{user.level?.name}}
+
{{user.level?.name}}
+
+ + +
@@ -266,8 +288,12 @@
- +
+ + +
diff --git a/kdb-web/src/app/modules/view/server/profile/profile.component.ts b/kdb-web/src/app/modules/view/server/profile/profile.component.ts index c06e8387..6625f101 100644 --- a/kdb-web/src/app/modules/view/server/profile/profile.component.ts +++ b/kdb-web/src/app/modules/view/server/profile/profile.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { Queries } from "../../../../models/graphql/queries.model"; -import { UserListQuery, UserWarningQuery } from "../../../../models/graphql/query.model"; +import { LevelListQuery, Query, UserListQuery, UserWarningQuery } from "../../../../models/graphql/query.model"; import { SpinnerService } from "../../../../services/spinner/spinner.service"; import { DataService } from "../../../../services/data/data.service"; import { User } from "../../../../models/data/user.model"; @@ -13,10 +13,11 @@ import { Server } from "../../../../models/data/server.model"; import { forkJoin, Subject, throwError } from "rxjs"; import { catchError, takeUntil } from "rxjs/operators"; import { Table } from "primeng/table"; -import { UserWarning } from "../../../../models/data/user_warning.model"; -import { LevelMutationResult, UpdateUserMutationResult, UserWarningMutationResult } from "../../../../models/graphql/result.model"; +import { UpdateUserMutationResult } from "../../../../models/graphql/result.model"; import { Mutations } from "../../../../models/graphql/mutations.model"; -import { ConfirmationDialogService } from "../../../../services/confirmation-dialog/confirmation-dialog.service"; +import { MenuItem } from "primeng/api"; +import { UserWarning } from "../../../../models/data/user_warning.model"; +import moment from "moment"; @Component({ selector: "app-profile", @@ -26,11 +27,13 @@ import { ConfirmationDialogService } from "../../../../services/confirmation-dia export class ProfileComponent implements OnInit, OnDestroy { user: User = { createdAt: "", modifiedAt: "" }; + levels!: MenuItem[]; private server: Server = {}; private author?: UserDTO; private clonedUserWarnings: UserWarning[] = []; public isEditingNewUserWarning: boolean = false; public isEditing: boolean = false; + public isModerator: boolean = false; private unsubscriber = new Subject(); @@ -47,6 +50,7 @@ export class ProfileComponent implements OnInit, OnDestroy { } public ngOnInit(): void { + this.isEditing = false; this.loadProfile(); } @@ -59,6 +63,18 @@ export class ProfileComponent implements OnInit, OnDestroy { } this.server = server; + this.data.query(Queries.levelQuery, { + serverId: server.id + }, + (data: Query) => { + return data.servers[0]; + } + ).subscribe(data => { + this.levels = data.levels.map(level => { + return { label: level.name, value: level }; + }); + }); + let authUser = await this.auth.getLoggedInUser(); this.spinner.showSpinner(); let user: UserDTO | null = authUser?.users?.find(u => u.server == server.id) ?? null; @@ -69,6 +85,7 @@ export class ProfileComponent implements OnInit, OnDestroy { return; } this.author = user; + this.isModerator = user?.isModerator; this.data.query(Queries.userProfile, { serverId: this.server.id, @@ -90,7 +107,6 @@ export class ProfileComponent implements OnInit, OnDestroy { ).subscribe(result => { this.user.userWarningCount = result.userWarningCount; this.user.userWarnings = result.userWarnings; - console.log(result); this.spinner.hideSpinner(); }); @@ -99,12 +115,16 @@ export class ProfileComponent implements OnInit, OnDestroy { }); } + public toogleEditUser() { + this.isEditing = !this.isEditing; + } + public updateUser() { - this.spinner.showSpinner(); this.spinner.showSpinner(); this.data.mutation(Mutations.updateUser, { id: this.user.id, xp: this.user.xp, + birthday: moment(this.user.birthday).format("DD.MM.YYYY"), levelId: this.user.level?.id, userWarnings: this.user.userWarnings?.map(userWarning => { return { @@ -112,15 +132,17 @@ export class ProfileComponent implements OnInit, OnDestroy { user: userWarning.user?.id ?? this.user.id, description: userWarning.description, author: userWarning.author?.id ?? this.author?.id - } + }; }) } ).pipe(catchError(err => { this.spinner.hideSpinner(); + this.isEditing = false; return throwError(err); })).subscribe(_ => { this.spinner.hideSpinner(); this.toastService.success(this.translate.instant("view.server.members.message.user_changed"), this.translate.instant("view.server.members.message.user_changed_d", { name: this.user.name })); + this.isEditing = false; this.loadProfile(); }); } @@ -180,7 +202,7 @@ export class ProfileComponent implements OnInit, OnDestroy { this.user.joinedVoiceChannels = []; } - addNewUserWarning(table: Table) { + public addNewUserWarning(table: Table) { const newWarning: UserWarning = { description: "", user: this.user @@ -200,11 +222,11 @@ export class ProfileComponent implements OnInit, OnDestroy { this.clonedUserWarnings[index] = { ...user }; } - deleteUserWarning(index: number) { + public deleteUserWarning(index: number) { this.user.userWarnings?.splice(index, 1); } - editSaveUserWarning(value: any, index: number) { + public editSaveUserWarning(value: any, index: number) { this.isEditingNewUserWarning = false; if (!value.value || !this.user.userWarnings || this.user.userWarnings[index] == this.clonedUserWarnings[index]) { return; @@ -213,7 +235,7 @@ export class ProfileComponent implements OnInit, OnDestroy { delete this.clonedUserWarnings[index]; } - editCancelUserWarning(index: number) { + public editCancelUserWarning(index: number) { if (this.user.userWarnings) { this.user.userWarnings[index] = this.clonedUserWarnings[index]; } diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index 2219275a..53e6c76a 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -122,6 +122,7 @@ } }, "common": { + "edit": "Bearbeiten", "user_warnings": "Verwarnungen", "author": "Autor", "404": "404 - Der Eintrag konnte nicht gefunden werden", @@ -484,6 +485,9 @@ } }, "profile": { + "message_count": "Anzahl Nachrichten", + "reaction_count": "Anzahl Reaktionen", + "birthday": "Geburtstag", "achievements": { "header": "Errungeschaften", "time": "Erreicht am" diff --git a/kdb-web/src/assets/version.json b/kdb-web/src/assets/version.json index e044326d..3ae731e3 100644 --- a/kdb-web/src/assets/version.json +++ b/kdb-web/src/assets/version.json @@ -1,7 +1,7 @@ { "WebVersion": { "Major": "1", - "Minor": "1", - "Micro": "dev402" + "Minor": "2", + "Micro": "0" } } \ No newline at end of file diff --git a/kdb-web/src/styles/primeng-fixes.scss b/kdb-web/src/styles/primeng-fixes.scss index 67f10cd6..81ad47dc 100644 --- a/kdb-web/src/styles/primeng-fixes.scss +++ b/kdb-web/src/styles/primeng-fixes.scss @@ -94,8 +94,13 @@ p-table { } } -.p-dropdown { - width: 100% !important; +.content-row { + p-dropdown, + .p-dropdown, + p-calendar, + .p-calendar, { + width: 100% !important; + } } .pi-sort-alt:before { diff --git a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss index fba55057..827d3933 100644 --- a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss @@ -683,4 +683,77 @@ p-inputNumber { background-color: $primaryBackgroundColor !important; } + + p-calendar > span > button { + background-color: $primaryHeaderColor !important; + border: 1px solid $primaryHeaderColor !important; + + &:focus { + box-shadow: none !important; + } + } + + .p-calendar { + .p-datepicker:not(.p-datepicker-inline) { + background-color: $secondaryBackgroundColor !important; + } + + .p-datepicker { + .p-datepicker-header { + color: $primaryHeaderColor !important; + background-color: $primaryBackgroundColor !important; + + .p-datepicker-title .p-datepicker-year, + .p-datepicker-title .p-datepicker-month, + .p-datepicker .p-datepicker-header .p-datepicker-title .p-datepicker-month { + color: $primaryTextColor !important; + + &:hover { + color: $primaryHeaderColor !important; + } + + &:focus { + box-shadow: none !important; + } + } + } + } + + table td > span { + color: $primaryTextColor !important; + + &:hover { + color: $primaryHeaderColor !important; + background-color: $primaryBackgroundColor !important; + } + + &:focus { + box-shadow: none !important; + } + } + + table td.p-datepicker-today > span { + color: $primaryHeaderColor !important; + background-color: $primaryBackgroundColor !important; + } + + table td > span.p-highlight { + color: $primaryHeaderColor !important; + background-color: $primaryBackgroundColor !important; + } + + .p-yearpicker .p-yearpicker-year, + .p-monthpicker .p-monthpicker-month:not(.p-disabled):not(.p-highlight) { + color: $primaryTextColor !important; + background-color: $secondaryBackgroundColor !important; + + &:hover { + color: $primaryHeaderColor !important; + } + + &:focus { + box-shadow: none !important; + } + } + } }