#410 #439

Merged
edraft merged 10 commits from #410 into dev 2023-11-19 12:57:44 +01:00
25 changed files with 504 additions and 278 deletions
Showing only changes of commit 74dba4b981 - Show all commits

View File

@ -4,13 +4,14 @@ from typing import Optional
import discord
from cpl_core.database import TableABC
from bot_data.model.scheduled_event_interval_enum import ScheduledEventIntervalEnum
from bot_data.model.server import Server
class ScheduledEvent(TableABC):
def __init__(
self,
interval: str,
interval: ScheduledEventIntervalEnum,
name: str,
description: str,
channel_id: int,
@ -43,11 +44,11 @@ class ScheduledEvent(TableABC):
return self._id
@property
def interval(self) -> str:
def interval(self) -> ScheduledEventIntervalEnum:
return self._interval
@interval.setter
def interval(self, value: str):
def interval(self, value: ScheduledEventIntervalEnum):
self._interval = value
@property
@ -145,11 +146,11 @@ class ScheduledEvent(TableABC):
return str(
f"""
INSERT INTO `ScheduledEvents` (
`Interval`, `Name`, `Description`, `ChannelId`, `StartTime`, `EndTime`, `EntityType`, `Location`, `ServerId`,
`Interval`, `Name`, `Description`, `ChannelId`, `StartTime`, `EndTime`, `EntityType`, `Location`, `ServerId`
) VALUES (
'{self._interval}',
'{self._interval.value}',
'{self._name}',
'{self._description}',
{"NULL" if self._description is None else f"'{self._description}'"},
{"NULL" if self._channel_id is None else f"'{self._channel_id}'"},
'{self._start_time}',
{"NULL" if self._end_time is None else f"'{self._end_time}'"},
@ -165,7 +166,7 @@ class ScheduledEvent(TableABC):
return str(
f"""
UPDATE `ScheduledEvents`
SET `Interval` = '{self._interval}',
SET `Interval` = '{self._interval.value}',
`Name` = '{self._name}',
`Description` = '{self._start_time}',
`ChannelId` = {"NULL" if self._channel_id is None else f"'{self._channel_id}'"},

View File

@ -0,0 +1,8 @@
from enum import Enum
class ScheduledEventIntervalEnum(Enum):
daily = "daily"
weekly = "weekly"
monthly = "monthly"
yearly = "yearly"

View File

@ -1,9 +1,9 @@
CREATE TABLE IF NOT EXISTS `ScheduledEvents`
(
`Id` BIGINT NOT NULL AUTO_INCREMENT,
`Interval` VARCHAR(255) NOT NULL,
`Interval` ENUM ('daily', 'weekly', 'monthly', 'yearly') NOT NULL,
`Name` VARCHAR(255) NOT NULL,
`Description` VARCHAR(255) NOT NULL,
`Description` VARCHAR(255) NULL,
`ChannelId` BIGINT NULL,
`StartTime` DATETIME(6) NOT NULL,
`EndTime` DATETIME(6) NULL,
@ -19,9 +19,9 @@ CREATE TABLE IF NOT EXISTS `ScheduledEvents`
CREATE TABLE IF NOT EXISTS `ScheduledEventsHistory`
(
`Id` BIGINT(20) NOT NULL,
`Interval` VARCHAR(255) NOT NULL,
`Interval` ENUM ('daily', 'weekly', 'monthly', 'yearly') NOT NULL,
`Name` VARCHAR(255) NOT NULL,
`Description` VARCHAR(255) NOT NULL,
`Description` VARCHAR(255) NULL,
`ChannelId` BIGINT NULL,
`StartTime` DATETIME(6) NOT NULL,
`EndTime` DATETIME(6) NULL,

View File

@ -7,6 +7,7 @@ from bot_core.logging.database_logger import DatabaseLogger
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.scheduled_event_repository_abc import ScheduledEventRepositoryABC
from bot_data.model.scheduled_event import ScheduledEvent
from bot_data.model.scheduled_event_interval_enum import ScheduledEventIntervalEnum
class ScheduledEventRepositoryService(ScheduledEventRepositoryABC):
@ -32,17 +33,17 @@ class ScheduledEventRepositoryService(ScheduledEventRepositoryABC):
def _scheduled_event_from_result(self, sql_result: tuple) -> ScheduledEvent:
return ScheduledEvent(
self._get_value_from_result(sql_result[0]), # interval
self._get_value_from_result(sql_result[1]), # name
self._get_value_from_result(sql_result[2]), # description
int(self._get_value_from_result(sql_result[3])), # channel_id
self._get_value_from_result(sql_result[4]), # start_time
self._get_value_from_result(sql_result[5]), # end_time
self._get_value_from_result(sql_result[6]), # entity_type
self._get_value_from_result(sql_result[7]), # location
self._servers.get_server_by_id((sql_result[8])), # server
self._get_value_from_result(sql_result[9]), # created_at
self._get_value_from_result(sql_result[10]), # modified_at
self._get_value_from_result(ScheduledEventIntervalEnum(sql_result[1])), # interval
self._get_value_from_result(sql_result[2]), # name
self._get_value_from_result(sql_result[3]), # description
int(self._get_value_from_result(sql_result[4])), # channel_id
self._get_value_from_result(sql_result[5]), # start_time
self._get_value_from_result(sql_result[6]), # end_time
self._get_value_from_result(sql_result[7]), # entity_type
self._get_value_from_result(sql_result[8]), # location
self._servers.get_server_by_id((sql_result[9])), # server
self._get_value_from_result(sql_result[10]), # created_at
self._get_value_from_result(sql_result[11]), # modified_at
id=self._get_value_from_result(sql_result[0]),
)

View File

@ -63,7 +63,9 @@ class FilterABC(ABC):
self.__setattr__(f"_{attr}", attr_type(values[attr]))
@staticmethod
def _filter_by_attributes(attrs: list[Callable], values: List[T]) -> List[R]:
def _filter_by_attributes(attrs: list[dict], values: List[T]) -> List[R]:
for attr in attrs:
values = values.where(attr)
if attr["attr"] is None:
continue
values = values.where(attr["func"])
return values

View File

@ -51,15 +51,15 @@ class ScheduledEventFilter(FilterABC):
query = self._filter_by_attributes(
[
lambda x: x.id == self._id,
lambda x: x.interval == self._interval,
lambda x: x.name == self._name,
lambda x: x.description == self._description,
lambda x: x.channel_id == self._channel_id,
lambda x: x.start_time == self._start_time,
lambda x: x.end_time == self._end_time,
lambda x: x.entity_type == self._entity_type,
lambda x: x.location == self._location,
{"attr": self._id, "func": lambda x: x.id == self._id},
{"attr": self._interval, "func": lambda x: x.interval == self._interval},
{"attr": self._name, "func": lambda x: x.name == self._name},
{"attr": self._description, "func": lambda x: x.description == self._description},
{"attr": self._channel_id, "func": lambda x: x.channel_id == self._channel_id},
{"attr": self._start_time, "func": lambda x: x.start_time == self._start_time},
{"attr": self._end_time, "func": lambda x: x.end_time == self._end_time},
{"attr": self._entity_type, "func": lambda x: x.entity_type == self._entity_type},
{"attr": self._location, "func": lambda x: x.location == self._location},
],
query,
)

View File

@ -4,6 +4,7 @@ from bot_graphql.mutations.achievement_mutation import AchievementMutation
from bot_graphql.mutations.auto_role_mutation import AutoRoleMutation
from bot_graphql.mutations.auto_role_rule_mutation import AutoRoleRuleMutation
from bot_graphql.mutations.level_mutation import LevelMutation
from bot_graphql.mutations.scheduled_event_mutation import ScheduledEventMutation
from bot_graphql.mutations.server_config_mutation import ServerConfigMutation
from bot_graphql.mutations.short_role_name_mutation import ShortRoleNameMutation
from bot_graphql.mutations.technician_config_mutation import TechnicianConfigMutation
@ -23,6 +24,7 @@ class Mutation(MutationType):
achievement_mutation: AchievementMutation,
user_joined_game_server: UserJoinedGameServerMutation,
technician_config: TechnicianConfigMutation,
scheduled_event: ScheduledEventMutation,
server_config: ServerConfigMutation,
short_role_name_mutation: ShortRoleNameMutation,
):
@ -36,4 +38,5 @@ class Mutation(MutationType):
self.set_field("userJoinedGameServer", lambda *_: user_joined_game_server)
self.set_field("shortRoleName", lambda *_: short_role_name_mutation)
self.set_field("technicianConfig", lambda *_: technician_config)
self.set_field("scheduledEvent", lambda *_: scheduled_event)
self.set_field("serverConfig", lambda *_: server_config)

View File

@ -1,9 +1,12 @@
from datetime import datetime
from cpl_core.database.context import DatabaseContextABC
from cpl_discord.service import DiscordBotServiceABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.scheduled_event_repository_abc import ScheduledEventRepositoryABC
from bot_data.model.scheduled_event import ScheduledEvent
from bot_data.model.scheduled_event_interval_enum import ScheduledEventIntervalEnum
from bot_data.model.user_role_enum import UserRoleEnum
from bot_graphql.abc.query_abc import QueryABC
from modules.permission.service.permission_service import PermissionService
@ -35,14 +38,14 @@ class ScheduledEventMutation(QueryABC):
self._can_user_mutate_data(server, UserRoleEnum.moderator)
scheduled_event = ScheduledEvent(
input["interval"],
ScheduledEventIntervalEnum(input["interval"]),
input["name"],
input["description"],
input["channel_id"],
input["start_time"],
input["end_time"],
input["entity_type"],
input["location"],
input["description"] if "description" in input else None,
input["channelId"] if "channelId" in input else None,
datetime.strptime(input["startTime"], "%Y-%m-%dT%H:%M:%S.%fZ"),
datetime.strptime(input["endTime"], "%Y-%m-%dT%H:%M:%S.%fZ") if "endTime" in input else None,
input["entityType"],
input["location"] if "location" in input else None,
server,
)
@ -70,10 +73,10 @@ class ScheduledEventMutation(QueryABC):
scheduled_event.interval = input["interval"] if "interval" in input else scheduled_event.interval
scheduled_event.name = input["name"] if "name" in input else scheduled_event.name
scheduled_event.description = input["description"] if "description" in input else scheduled_event.description
scheduled_event.channel_id = input["channel_id"] if "channel_id" in input else scheduled_event.channel_id
scheduled_event.start_time = input["start_time"] if "start_time" in input else scheduled_event.start_time
scheduled_event.end_time = input["end_time"] if "end_time" in input else scheduled_event.end_time
scheduled_event.entity_type = input["entity_type"] if "entity_type" in input else scheduled_event.entity_type
scheduled_event.channel_id = input["channelId"] if "channelId" in input else scheduled_event.channel_id
scheduled_event.start_time = input["startTime"] if "startTime" in input else scheduled_event.start_time
scheduled_event.end_time = input["endTime"] if "endTime" in input else scheduled_event.end_time
scheduled_event.entity_type = input["entityType"] if "entityType" in input else scheduled_event.entity_type
scheduled_event.location = input["location"] if "location" in input else scheduled_event.location
self._scheduled_events.update_scheduled_event(scheduled_event)

View File

@ -1,56 +1,56 @@
{
"name": "web",
"version": "1.2.2",
"scripts": {
"ng": "ng",
"update-version": "ts-node update-version.ts",
"prestart": "npm run update-version",
"start": "ng serve",
"prebuild": "npm run update-version",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"gv": "echo $npm_package_version",
"predocker-build": "npm run update-version",
"docker-build": "export VERSION=$npm_package_version; ng build; docker build -t sh-edraft.de/sdb-web:$VERSION .",
"docker-build-dev": "export VERSION=$npm_package_version; ng build --configuration development; docker build -t sh-edraft.de/sdb-web:$VERSION .",
"docker-build-stage": "export VERSION=$npm_package_version; ng build --configuration staging; docker build -t sh-edraft.de/sdb-web:$VERSION ."
},
"private": true,
"dependencies": {
"@angular/animations": "^15.1.4",
"@angular/common": "^15.1.4",
"@angular/compiler": "^15.1.4",
"@angular/core": "^15.1.4",
"@angular/forms": "^15.1.4",
"@angular/platform-browser": "^15.1.4",
"@angular/platform-browser-dynamic": "^15.1.4",
"@angular/router": "^15.1.4",
"@auth0/angular-jwt": "^5.1.0",
"@microsoft/signalr": "^6.0.9",
"@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",
"socket.io-client": "^4.5.3",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.1.5",
"@angular/cli": "~15.1.5",
"@angular/compiler-cli": "^15.1.4",
"@types/jasmine": "~4.0.0",
"@types/node": "^18.11.9",
"jasmine-core": "~4.1.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"tslib": "^2.4.1",
"typescript": "~4.9.5"
}
}
"name": "web",
"version": "1.2.dev410",
"scripts": {
"ng": "ng",
"update-version": "ts-node update-version.ts",
"prestart": "npm run update-version",
"start": "ng serve",
"prebuild": "npm run update-version",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"gv": "echo $npm_package_version",
"predocker-build": "npm run update-version",
"docker-build": "export VERSION=$npm_package_version; ng build; docker build -t sh-edraft.de/sdb-web:$VERSION .",
"docker-build-dev": "export VERSION=$npm_package_version; ng build --configuration development; docker build -t sh-edraft.de/sdb-web:$VERSION .",
"docker-build-stage": "export VERSION=$npm_package_version; ng build --configuration staging; docker build -t sh-edraft.de/sdb-web:$VERSION ."
},
"private": true,
"dependencies": {
"@angular/animations": "^15.1.4",
"@angular/common": "^15.1.4",
"@angular/compiler": "^15.1.4",
"@angular/core": "^15.1.4",
"@angular/forms": "^15.1.4",
"@angular/platform-browser": "^15.1.4",
"@angular/platform-browser-dynamic": "^15.1.4",
"@angular/router": "^15.1.4",
"@auth0/angular-jwt": "^5.1.0",
"@microsoft/signalr": "^6.0.9",
"@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",
"socket.io-client": "^4.5.3",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.1.5",
"@angular/cli": "~15.1.5",
"@angular/compiler-cli": "^15.1.4",
"@types/jasmine": "~4.0.0",
"@types/node": "^18.11.9",
"jasmine-core": "~4.1.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"tslib": "^2.4.1",
"typescript": "~4.9.5"
}
}

View File

@ -1,4 +1,4 @@
export interface Discord {
export interface Discord {
guilds?: Guild[];
users?: DiscordUser[];
}
@ -21,7 +21,8 @@ export interface Channel {
export enum ChannelType {
category = "CategoryChannel",
text = "TextChannel",
voice = "VoiceChannel"
voice = "VoiceChannel",
stage = "StageChannel"
}
export interface Role {

View File

@ -9,12 +9,12 @@ export enum EventType {
export interface ScheduledEvent extends DataWithHistory {
id?: number;
interval?: string;
interval?: ScheduledEventInterval;
name?: string;
description?: string;
channelId?: string;
startTime?: string;
endTime?: string;
startTime?: Date;
endTime?: Date;
entityType?: EventType;
location?: string;
server?: Server;
@ -32,3 +32,10 @@ export interface ScheduledEventFilter {
location?: string;
server?: ServerFilter;
}
export enum ScheduledEventInterval {
daily = "daily",
weekly = "weekly",
monthly = "monthly",
yearly = "yearly"
}

View File

@ -185,14 +185,16 @@ export class Mutations {
endTime: $endTime,
entityType: $entityType,
location: $location,
serverId: $serverId}
) {
id
serverId: $serverId
}) {
interval
name
description
attribute
operator
value
channelId
startTime
endTime
entityType
location
server {
id
}
@ -215,12 +217,17 @@ export class Mutations {
location: $location,
serverId: $serverId}
) {
id
interval
name
description
attribute
operator
value
channelId
startTime
endTime
entityType
location
server {
id
}
}
}
}
@ -397,7 +404,6 @@ export class Mutations {
`;
static createUserWarning = `
mutation createUserWarning($name: String, $description: String, $attribute: String, $operator: String, $value: String, $serverId: ID) {
userWarning {

View File

@ -1,4 +1,4 @@
import { CommonModule } from "@angular/common";
import { CommonModule, DatePipe } from "@angular/common";
import { HttpClientModule } from "@angular/common/http";
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
@ -38,6 +38,8 @@ import { FileUploadModule } from "primeng/fileupload";
import { SelectButtonModule } from "primeng/selectbutton";
import { TabViewModule } from "primeng/tabview";
import { RadioButtonModule } from "primeng/radiobutton";
import { InputTextareaModule } from "primeng/inputtextarea";
import { InputMaskModule } from "primeng/inputmask";
const PrimeNGModules = [
@ -69,7 +71,9 @@ const PrimeNGModules = [
FileUploadModule,
SelectButtonModule,
TabViewModule,
RadioButtonModule
RadioButtonModule,
InputTextareaModule,
InputMaskModule
];
@NgModule({
@ -102,6 +106,9 @@ const PrimeNGModules = [
MultiSelectColumnsComponent,
FeatureFlagListComponent,
DataImportAndExportComponent
],
providers: [
DatePipe
]
})
export class SharedModule {

View File

@ -1,44 +1,92 @@
<div class="edit-dialog">
<p-dialog [header]="header" [(visible)]="visible" [modal]="true"
<p-dialog [contentStyle]="{'overflow':'visible'}" [header]="header" [(visible)]="visible" [modal]="true"
[draggable]="false" [resizable]="false"
[style]="{ width: '50vw' }">
[style]="{ width: '500px', height: '575px' }">
<form [formGroup]="inputForm">
<p-tabView [(activeIndex)]="activeIndex">
<p-tabPanel header="{{'view.server.scheduled_events.edit_dialog.location.tab_name' | translate}}">
<div *ngFor="let enum of EventType | keyvalue" class="field-checkbox">
<p-radioButton [inputId]="enum.key" [value]="enum.value" formControlName="entityType"></p-radioButton>
<label [for]="enum.key"
class="ml-2">{{'view.server.scheduled_events.edit_dialog.location.' + enum.key | translate}}</label>
<div class="form">
<div style="display: flex; flex-direction: column; gap: 5px;">
{{'view.server.scheduled_events.edit_dialog.location.interval' | translate}}:
<p-dropdown [options]="interval"
formControlName="interval"
placeholder="{{'view.server.scheduled_events.edit_dialog.location.interval' | translate}}"></p-dropdown>
</div>
<div class="type">
<div *ngFor="let enum of EventType | keyvalue" class="field-checkbox">
<p-radioButton [inputId]="enum.key" [value]="enum.value" formControlName="entityType"></p-radioButton>
<label [for]="enum.key"
class="ml-2">{{'view.server.scheduled_events.edit_dialog.location.' + enum.key | translate}}</label>
</div>
</div>
<div class="input">
<p-dropdown *ngIf="inputForm.controls.entityType.value == 1"
[options]="stageChannels"
optionLabel="name"
optionValue="id"
formControlName="channelId"
placeholder="{{'view.server.scheduled_events.edit_dialog.location.stage_input' | translate}}"></p-dropdown>
<p-dropdown *ngIf="inputForm.controls.entityType.value == 2"
[options]="voiceChannels"
optionLabel="name"
optionValue="id"
formControlName="channelId"
placeholder="{{'view.server.scheduled_events.edit_dialog.location.voice_input' | translate}}"></p-dropdown>
<input *ngIf="inputForm.controls.entityType.value == 3" pInputText class="table-edit-input"
formControlName="location"
placeholder="{{'view.server.scheduled_events.edit_dialog.location.somewhere_else' | translate}}">
</div>
</div>
</p-tabPanel>
<p-tabPanel header="{{'view.server.scheduled_events.edit_dialog.event_info.tab_name' | translate}}">
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem
aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
Nemo
enim
ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos
qui
ratione voluptatem sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam eius modi.
</p>
<div class="form">
<div style="display: flex; flex-direction: column; gap: 5px;">
{{'view.server.scheduled_events.edit_dialog.event_info.event_topic' | translate}}:
<input pInputText class="table-edit-input"
formControlName="name"
placeholder="{{'view.server.scheduled_events.edit_dialog.event_info.event_topic_input' | translate}}">
</div>
<div style="display: flex; flex-direction: column; gap: 5px;">
{{'view.server.scheduled_events.edit_dialog.event_info.start_date_time' | translate}}:
<p-calendar formControlName="startTime" dateFormat="dd.mm.yy" [showIcon]="true"
[showTime]="true" [stepMinute]="15"></p-calendar>
</div>
<div style="display: flex; flex-direction: column; gap: 5px;">
{{'view.server.scheduled_events.edit_dialog.event_info.end_date_time' | translate}}:
<p-calendar formControlName="endTime" dateFormat="dd.mm.yy" [showIcon]="true"
[showTime]="true" [stepMinute]="15"></p-calendar>
</div>
<div style="display: flex; flex-direction: column; gap: 5px;">
{{'view.server.scheduled_events.edit_dialog.event_info.description' | translate}}:
<textarea rows="5" cols="30" pInputTextarea [autoResize]="true"
class="table-edit-input"
formControlName="description"
placeholder="{{'view.server.scheduled_events.edit_dialog.event_info.description_input' | translate}}"></textarea>
</div>
</div>
</p-tabPanel>
</p-tabView>
<ng-template pTemplate="footer">
<div style="display: flex;">
<div class="btn-wrapper" style="flex: 1;">
<button pButton label="{{'common.back' | translate}}" class="text-btn"
(click)="back()" [disabled]="activeIndex == 0"></button>
</div>
<div class="btn-wrapper">
<button pButton label="{{'common.abort' | translate}}" class="btn danger-btn"
(click)="visible = false"></button>
<button *ngIf="activeIndex < 1" pButton label="{{'common.continue' | translate}}" class="btn"
(click)="next()"></button>
<button *ngIf="activeIndex == 1" pButton label="{{'common.save' | translate}}" class="btn"
(click)="saveEvent()" [disabled]="!inputForm.valid"></button>
</div>
</div>
</ng-template>
</form>
<ng-template pTemplate="footer">
<div style="display: flex;">
<div class="btn-wrapper" style="flex: 1;">
<button pButton label="{{'common.back' | translate}}" class="text-btn"
(click)="back()" [disabled]="activeIndex == 0"></button>
</div>
<div class="btn-wrapper">
<button pButton label="{{'common.abort' | translate}}" class="btn danger-btn"
(click)="visible = false"></button>
<button *ngIf="activeIndex < 1" pButton label="{{'common.continue' | translate}}" class="btn"
(click)="next()" [disabled]="!inputForm"></button>
<button *ngIf="activeIndex == 1" pButton label="{{'common.save' | translate}}" class="btn"
(click)="saveEvent()" [disabled]="!inputForm.valid"></button>
</div>
</div>
</ng-template>
</p-dialog>
</div>

View File

@ -1,14 +1,26 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { EventType, ScheduledEvent } from "../../../../../../models/data/scheduled_events.model";
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import {
EventType,
ScheduledEvent,
ScheduledEventInterval
} from "../../../../../../models/data/scheduled_events.model";
import { TranslateService } from "@ngx-translate/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
import { SingleDiscordQuery } from "../../../../../../models/graphql/query.model";
import { Queries } from "../../../../../../models/graphql/queries.model";
import { Channel, ChannelType, Guild } from "../../../../../../models/data/discord.model";
import { DataService } from "../../../../../../services/data/data.service";
import { SpinnerService } from "../../../../../../services/spinner/spinner.service";
import { ActivatedRoute } from "@angular/router";
import { Server } from "../../../../../../models/data/server.model";
import { DatePipe } from "@angular/common";
@Component({
selector: "app-edit-scheduled-event-dialog",
templateUrl: "./edit-scheduled-event-dialog.component.html",
styleUrls: ["./edit-scheduled-event-dialog.component.scss"]
})
export class EditScheduledEventDialogComponent {
export class EditScheduledEventDialogComponent implements OnInit {
@Input() event?: ScheduledEvent;
@Output() save = new EventEmitter<ScheduledEvent>();
@ -19,8 +31,9 @@ export class EditScheduledEventDialogComponent {
set visible(val: boolean) {
if (!val) {
this.event = undefined;
this.inputForm.reset();
this.activeIndex = 0;
}
this.visible = val;
}
get header() {
@ -32,26 +45,118 @@ export class EditScheduledEventDialogComponent {
}
public activeIndex: number = 0;
public inputForm = this.fb.group({
id: new FormControl<number | undefined>(this.event?.id),
interval: new FormControl<string | undefined>(this.event?.interval, [Validators.required]),
entityType: new FormControl<number | undefined>(this.event?.entityType, [Validators.required]),
channelId: new FormControl<string | undefined>(this.event?.channelId, this.event?.entityType == EventType.voice || this.event?.entityType == EventType.stageInstance ? [Validators.required] : []),
location: new FormControl<string | undefined>(this.event?.location, this.event?.entityType == EventType.external ? [Validators.required] : []),
name: new FormControl<string | undefined>(this.event?.name, [Validators.required]),
startTime: new FormControl<string | undefined>(this.event?.startTime, [Validators.required]),
endTime: new FormControl<string | undefined>(this.event?.endTime),
description: new FormControl<string | undefined>(this.event?.description)
});
public inputForm!: FormGroup<{
entityType: FormControl<number | undefined | null>;
name: FormControl<string | undefined | null>;
description: FormControl<string | undefined | null>;
interval: FormControl<ScheduledEventInterval | undefined | null>;
location: FormControl<string | undefined | null>;
startTime: FormControl<Date | undefined | null>;
id: FormControl<number | undefined | null>;
endTime: FormControl<Date | undefined | null>;
channelId: FormControl<string | undefined | null>
}>;
server: Server = {};
public voiceChannels: Channel[] = [];
public stageChannels: Channel[] = [];
public guild: Guild = { channels: [], emojis: [], roles: [] };
public times: string[] = [];
public now = new Date();
public interval = Object.keys(ScheduledEventInterval).map(k => (
{
label: this.translate.instant(`view.server.scheduled_events.edit_dialog.location.intervals.${k}`),
value: k
}
));
constructor(
private translate: TranslateService,
private fb: FormBuilder
private fb: FormBuilder,
private data: DataService,
private spinner: SpinnerService,
private route: ActivatedRoute,
private datePipe: DatePipe
) {
for (let i = 0; i < 25; i++) {
let time = "";
if (i < 10) {
time = `0${i}`;
} else {
time = `${i}`;
}
this.times.push(`${time}:00`);
this.times.push(`${time}:15`);
this.times.push(`${time}:30`);
this.times.push(`${time}:45`);
}
this.setInputForm();
}
public ngOnInit() {
this.data.getServerFromRoute(this.route).then(server => {
this.server = server;
this.spinner.showSpinner();
this.data.query<SingleDiscordQuery>(Queries.guildsQuery, {
id: server?.discordId
}
).subscribe(data => {
if (data.discord.guilds) {
this.guild = data.discord.guilds[0];
}
this.voiceChannels = this.guild.channels.filter(x => x.type === ChannelType.voice);
this.stageChannels = this.guild.channels.filter(x => x.type === ChannelType.stage);
this.setInputForm();
this.spinner.hideSpinner();
});
});
}
public setInputForm() {
if (this.now.getMinutes() % 15 != 0) {
if (this.now.getMinutes() < 15) {
this.now.setMinutes(15);
} else if (this.now.getMinutes() < 30) {
this.now.setMinutes(30);
} else if (this.now.getMinutes() < 45) {
this.now.setMinutes(45);
} else if (this.now.getMinutes() > 45) {
this.now.setMinutes(0);
this.now.setHours(this.now.getHours() + 1);
}
}
this.now.setSeconds(0);
this.inputForm = this.fb.group({
id: new FormControl<number | undefined>(this.event?.id),
interval: new FormControl<ScheduledEventInterval | undefined>(this.event?.interval, [Validators.required]),
entityType: new FormControl<number | undefined>(this.event?.entityType, [Validators.required]),
channelId: new FormControl<string | undefined>(this.event?.channelId, this.event?.entityType == EventType.voice || this.event?.entityType == EventType.stageInstance ? [Validators.required] : []),
location: new FormControl<string | undefined>(this.event?.location, this.event?.entityType == EventType.external ? [Validators.required] : []),
name: new FormControl<string | undefined>(this.event?.name, [Validators.required]),
startTime: new FormControl<Date | undefined>(this.event?.startTime ? new Date(this.event.startTime) : this.now, [Validators.required]),
endTime: new FormControl<Date | undefined>(this.event?.endTime ? new Date(this.event.endTime) : undefined),
description: new FormControl<string | undefined>(this.event?.description)
});
}
public saveEvent() {
this.event = {
id: this.inputForm.controls.id.value ?? undefined,
interval: this.inputForm.controls.interval.value ?? undefined,
name: this.inputForm.controls.name.value ?? undefined,
description: this.inputForm.controls.description.value ?? undefined,
channelId: this.inputForm.controls.channelId.value ?? undefined,
startTime: this.inputForm.controls.startTime.value ?? undefined,
endTime: this.inputForm.controls.endTime.value ?? undefined,
entityType: this.inputForm.controls.entityType.value ?? undefined,
location: this.inputForm.controls.location.value ?? undefined,
server: this.server
};
this.save.emit(this.event);
this.event = undefined;
}
public next() {

View File

@ -1,4 +1,5 @@
<app-edit-scheduled-event-dialog [event]="editableScheduledEvent" (save)="onRowEditSave($event)"></app-edit-scheduled-event-dialog>
<app-edit-scheduled-event-dialog [event]="editableScheduledEvent"
(save)="onRowEditSave($event)"></app-edit-scheduled-event-dialog>
<h1>
{{'view.server.scheduled_events.header' | translate}}
@ -123,7 +124,7 @@
</form>
</th>
<th class="table-header-small"></th>
<th hideable-th="interval" [parent]="this"></th>
<th hideable-th="name" [parent]="this">
<form [formGroup]="filterForm">
@ -132,12 +133,24 @@
</form>
</th>
<th class="table-header-small"></th>
<th class="table-header-small"></th>
<th class="table-header-small"></th>
<th class="table-header-small"></th>
<th class="table-header-small"></th>
<th class="table-header-small"></th>
<th hideable-th="description" [parent]="this">
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="description"
placeholder="{{'common.description' | translate}}">
</form>
</th>
<th hideable-th="channel_id" [parent]="this">
<form [formGroup]="filterForm">
<input type="number" pInputText formControlName="channelId"
placeholder="{{'common.channel_id' | translate}}">
</form>
</th>
<th hideable-th="start_time" [parent]="this"></th>
<th hideable-th="end_time" [parent]="this"></th>
<th hideable-th="type" [parent]="this"></th>
<th hideable-th="location" [parent]="this"></th>
<th class="table-header-small-dropdown"></th>
<th class="table-header-small-dropdown"></th>
<th class="table-header-actions"></th>
@ -148,133 +161,56 @@
<tr [pEditableRow]="scheduledEvent">
<td hideable-td="id" [parent]="this">
<span class="p-column-title">{{'common.id' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
{{scheduledEvent.id}}
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.id}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.id}}
</td>
<td hideable-td="interval" [parent]="this">
<span class="p-column-title">{{'common.interval' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.interval">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.interval}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.interval}}
</td>
<td hideable-td="name" [parent]="this">
<span class="p-column-title">{{'common.name' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.name">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.name}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.name}}
</td>
<td hideable-td="description" [parent]="this">
<span class="p-column-title">{{'common.description' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.description">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.description}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.description}}
</td>
<td hideable-td="channel_id" [parent]="this">
<span class="p-column-title">{{'common.channel_id' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.channel_id">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.channel_id}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.channelId}}
</td>
<td hideable-td="start_time" [parent]="this">
<span class="p-column-title">{{'common.start_time' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.start_time">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.start_time}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.startTime}}
</td>
<td hideable-td="end_time" [parent]="this">
<span class="p-column-title">{{'common.end_time' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.end_time">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.end_time}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.endTime}}
</td>
<td hideable-td="type" [parent]="this">
<span class="p-column-title">{{'common.type' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.type">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.type}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.entityType}}
</td>
<td hideable-td="location" [parent]="this">
<span class="p-column-title">{{'common.location' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="scheduledEvent.location">
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.location}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.location}}
</td>
<td>
<span class="p-column-title">{{'common.created_at' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
{{scheduledEvent.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.createdAt | date:'dd.MM.yy HH:mm'}}
</td>
<td>
<span class="p-column-title">{{'common.modified_at' | translate}}:</span>
<p-cellEditor>
<ng-template pTemplate="input">
{{scheduledEvent.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{scheduledEvent.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
{{scheduledEvent.modifiedAt | date:'dd.MM.yy HH:mm'}}
</td>
<td>
<div class="btn-wrapper">

View File

@ -118,7 +118,7 @@ export class ScheduledEventsComponent extends ComponentWithTable implements OnIn
startTime: new FormControl<string | null>(null),
endTime: new FormControl<string | null>(null),
entityType: new FormControl<EventType | null>(null),
location: new FormControl<string | null>(null),
location: new FormControl<string | null>(null)
});
this.filterForm.valueChanges.pipe(
@ -221,7 +221,15 @@ export class ScheduledEventsComponent extends ComponentWithTable implements OnIn
if (this.isEditingNew) {
this.spinner.showSpinner();
this.data.mutation<ScheduledEventMutationResult>(Mutations.createScheduledEvent, {
id: newScheduledEvent.id,
interval: newScheduledEvent.interval,
name: newScheduledEvent.name,
description: newScheduledEvent.description,
channelId: newScheduledEvent.channelId,
startTime: newScheduledEvent.startTime,
endTime: newScheduledEvent.endTime,
entityType: newScheduledEvent.entityType,
location: newScheduledEvent.location,
serverId: this.server.id
}
).pipe(catchError(err => {
@ -240,7 +248,15 @@ export class ScheduledEventsComponent extends ComponentWithTable implements OnIn
this.spinner.showSpinner();
this.data.mutation<ScheduledEventMutationResult>(Mutations.updateScheduledEvent, {
id: newScheduledEvent.id,
interval: newScheduledEvent.interval,
name: newScheduledEvent.name,
description: newScheduledEvent.description,
channelId: newScheduledEvent.channelId,
startTime: newScheduledEvent.startTime,
endTime: newScheduledEvent.endTime,
entityType: newScheduledEvent.entityType,
location: newScheduledEvent.location,
serverId: this.server.id
}
).pipe(catchError(err => {
this.spinner.hideSpinner();
@ -250,6 +266,7 @@ export class ScheduledEventsComponent extends ComponentWithTable implements OnIn
this.toastService.success(this.translate.instant("view.server.ScheduledEvents.message.scheduled_event_update"), this.translate.instant("view.server.ScheduledEvents.message.scheduled_event_update_d", { name: newScheduledEvent.name }));
this.loadNextPage();
});
this.editableScheduledEvent = undefined;
}
public deleteScheduledEvent(ScheduledEvent: ScheduledEvent): void {
@ -272,6 +289,7 @@ export class ScheduledEventsComponent extends ComponentWithTable implements OnIn
}
public addScheduledEvent(table: Table): void {
this.isEditingNew = true;
this.editableScheduledEvent = JSON.parse(JSON.stringify(this.newScheduledEventTemplate));
// const newScheduledEvent = JSON.parse(JSON.stringify(this.newScheduledEventTemplate));
//

View File

@ -1,10 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { ScheduledEventsRoutingModule } from './scheduled-events-routing.module';
import { ScheduledEventsRoutingModule } from "./scheduled-events-routing.module";
import { ScheduledEventsComponent } from "./components/scheduled-events/scheduled-events.component";
import { SharedModule } from "../../../shared/shared.module";
import { EditScheduledEventDialogComponent } from './components/edit-scheduled-event-dialog/edit-scheduled-event-dialog.component';
import {
EditScheduledEventDialogComponent
} from "./components/edit-scheduled-event-dialog/edit-scheduled-event-dialog.component";
@NgModule({
@ -16,6 +18,8 @@ import { EditScheduledEventDialogComponent } from './components/edit-scheduled-e
CommonModule,
SharedModule,
ScheduledEventsRoutingModule
]
],
exports: []
})
export class ScheduledEventsModule { }
export class ScheduledEventsModule {
}

View File

@ -4,7 +4,7 @@ import { BehaviorSubject, forkJoin, Observable } from "rxjs";
import { AuthRoles } from "../../models/auth/auth-roles.enum";
import { AuthService } from "../auth/auth.service";
import { TranslateService } from "@ngx-translate/core";
import { NavigationEnd, Router } from "@angular/router";
import { Router } from "@angular/router";
import { ThemeService } from "../theme/theme.service";
import { Server } from "../../models/data/server.model";
import { UserDTO } from "../../models/auth/auth-user.dto";
@ -113,7 +113,7 @@ export class SidebarService {
this.serverScheduledEvents = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.scheduled_events") : "",
icon: "pi pi-calender",
icon: "pi pi-calendar",
visible: true,
routerLink: `server/${this.server?.id}/scheduled-events`
};

View File

@ -335,6 +335,7 @@
"config": "Konfiguration",
"dashboard": "Dashboard",
"members": "Mitglieder",
"scheduled_events": "Geplante Events",
"server": {
"achievements": "Errungenschaften",
"auto_roles": "Auto Rollen",
@ -343,6 +344,7 @@
"levels": "Level",
"members": "Mitglieder",
"profile": "Dein Profil",
"scheduled_events": "Geplante Events",
"short_role_names": "Rollen Kürzel"
},
"server_empty": "Kein Server ausgewählt",
@ -551,16 +553,24 @@
"add_header": "Event hinzufügen",
"edit_header": "Event bearbeiten",
"event_info": {
"description": "Beschreibung",
"description_input": "Erzähl den Leuten ein wenig mehr über dein Event. Markdown, neue Zeilen und Links werden unterstützt.",
"event_topic": "Thema",
"event_topic_input": "Worum geht es bei deinem Event?",
"header": "Worum geht es bei deinem Event?",
"start_date": "Startdatum",
"start_time": "Startzeit",
"start_date_time": "Startzeitpunkt",
"end_date_time": "Endzeitpunkt",
"tab_name": "Eventinformationen"
},
"location": {
"header": "Wo ist dein Event?",
"interval": "Interval",
"intervals": {
"daily": "Täglich",
"weekly": "Wöchentlich",
"monthly": "Monatlich",
"yearly": "Jährlich"
},
"somewhere_else": "Irgendwo anders",
"somewhere_else_input": "Ort eingeben",
"stage": "Stage-Kanal",

View File

@ -338,6 +338,7 @@
"levels": "Level",
"members": "Members",
"profile": "Your profile",
"scheduled_events": "Scheduled events",
"short_role_names": "Short role names"
},
"server_empty": "No server selected",

View File

@ -1,7 +1,7 @@
{
"WebVersion": {
"Major": "1",
"Minor": "2",
"Micro": "2"
}
}
"WebVersion": {
"Major": "1",
"Minor": "2",
"Micro": "dev410"
}
}

View File

@ -689,8 +689,42 @@ p-inputNumber {
}
}
}
}
.form {
display: flex;
flex-direction: column;
gap: 20px;
textarea {
min-height: 101px !important;
&:focus {
box-shadow: none !important;
}
}
.type {
display: flex;
flex-direction: column;
gap: 10px;
.field-checkbox {
display: flex;
gap: 15px;
}
}
input,
.p-dropdown {
width: 100%;
}
}
}
p-calendar {
.p-calendar {
width: 100% !important;
}
}

View File

@ -113,3 +113,4 @@ p-table {
.pi-sort-amount-down:before {
content: "\e913" !important;
}

View File

@ -705,6 +705,8 @@
}
.p-datepicker {
color: $primaryTextColor !important;
.p-datepicker-header {
color: $primaryHeaderColor !important;
background-color: $primaryBackgroundColor !important;
@ -764,6 +766,19 @@
}
.edit-dialog {
textarea {
background-color: $secondaryBackgroundColor;
color: $primaryTextColor;
&:hover {
border-color: $primaryHeaderColor;
}
&:focus {
border-color: $primaryHeaderColor;
}
}
.p-dialog-content {
.p-tabview {
.p-tabview-nav li .p-tabview-nav-link:not(.p-disabled):focus {
@ -784,4 +799,19 @@
}
}
}
.p-radiobutton {
.p-radiobutton-box.p-highlight {
border-color: $primaryHeaderColor !important;
background: $primaryHeaderColor !important;
}
.p-radiobutton-box:not(.p-disabled):not(.p-highlight):hover {
border-color: $primaryHeaderColor !important;
}
.p-radiobutton-box:not(.p-disabled).p-focus {
box-shadow: none !important;
}
}
}