Neues Kalendermodul und DevExpress Übersetzungen

This commit is contained in:
2026-07-09 11:39:24 +02:00
parent 22c576af7b
commit 4d016f3ab0
29 changed files with 922 additions and 313 deletions

View File

@@ -193,6 +193,7 @@
<HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.4.1.0\lib\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath> <HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.4.1.0\lib\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.FileSystemGlobbing, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL" />
<Reference Include="Microsoft.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebHelpers.3.3.0\lib\net45\Microsoft.Web.Helpers.dll</HintPath> <HintPath>..\packages\Microsoft.AspNet.WebHelpers.3.3.0\lib\net45\Microsoft.Web.Helpers.dll</HintPath>
</Reference> </Reference>
@@ -400,6 +401,7 @@
<Compile Include="Util\JsonPerson.cs" /> <Compile Include="Util\JsonPerson.cs" />
<Compile Include="Util\SchedulerUtils\CustomAppointmentTemplateContainer.cs" /> <Compile Include="Util\SchedulerUtils\CustomAppointmentTemplateContainer.cs" />
<Compile Include="Util\SchedulerUtils\EmployeeAvailabilityObject.cs" /> <Compile Include="Util\SchedulerUtils\EmployeeAvailabilityObject.cs" />
<Compile Include="Util\SchedulerUtils\OwnSoftSchedulerLocalizer.cs" />
<Compile Include="Util\ServiceRecord2Validation.cs" /> <Compile Include="Util\ServiceRecord2Validation.cs" />
<Compile Include="Util\SignatureUtils.cs" /> <Compile Include="Util\SignatureUtils.cs" />
<Compile Include="Util\SupportConceptStatisticsData.cs" /> <Compile Include="Util\SupportConceptStatisticsData.cs" />

View File

@@ -12641,4 +12641,13 @@ a.text-bewo-report:hover, a.text-bewo-report:focus {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='25' height='25' viewBox='0 0 20 20'%3E%3Cg %3E%3Cpolygon fill='%23FFCA00' points='20 10 10 0 0 0 20 20'/%3E%3Cpolygon fill='%23FFCA00' points='0 10 0 20 10 20'/%3E%3C/g%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='25' height='25' viewBox='0 0 20 20'%3E%3Cg %3E%3Cpolygon fill='%23FFCA00' points='20 10 10 0 0 0 20 20'/%3E%3Cpolygon fill='%23FFCA00' points='0 10 0 20 10 20'/%3E%3C/g%3E%3C/svg%3E");
} }
@media (max-width: 575.98px) {
.btn-sm-auto {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
}
/*# sourceMappingURL=style.css.map */ /*# sourceMappingURL=style.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -255,4 +255,13 @@ $font-family-sans-serif: 'Signika Negative', sans-serif;
.bewo-dev-bg { .bewo-dev-bg {
background-color: #000000; background-color: #000000;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='25' height='25' viewBox='0 0 20 20'%3E%3Cg %3E%3Cpolygon fill='%23FFCA00' points='20 10 10 0 0 0 20 20'/%3E%3Cpolygon fill='%23FFCA00' points='0 10 0 20 10 20'/%3E%3C/g%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='25' height='25' viewBox='0 0 20 20'%3E%3Cg %3E%3Cpolygon fill='%23FFCA00' points='20 10 10 0 0 0 20 20'/%3E%3Cpolygon fill='%23FFCA00' points='0 10 0 20 10 20'/%3E%3C/g%3E%3C/svg%3E");
}
@media(max-width: 575.98px) {
.btn-sm-auto {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
} }

View File

@@ -121,6 +121,7 @@ namespace BeWoPlanerMobil.Controllers
Model.PossibleCustomers = CustomerService.GetAllAuthorizedCompactCustomers(); Model.PossibleCustomers = CustomerService.GetAllAuthorizedCompactCustomers();
Model.PossibleResources = KalenderService.GetAllResources(); Model.PossibleResources = KalenderService.GetAllResources();
// Ressourcen
if(Model.HasRightToInsertRessourceAppointments || Model.HasRightToViewAllResourceAppointments) if(Model.HasRightToInsertRessourceAppointments || Model.HasRightToViewAllResourceAppointments)
{ {
var allResources = KalenderService.GetAllResources().OrderBy(r => r.Name).ToList(); var allResources = KalenderService.GetAllResources().OrderBy(r => r.Name).ToList();
@@ -135,6 +136,7 @@ namespace BeWoPlanerMobil.Controllers
Model.ResourceCategories2Resources = categories2Resources.OrderBy(category2Resources => category2Resources.Key.DisplayName).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); Model.ResourceCategories2Resources = categories2Resources.OrderBy(category2Resources => category2Resources.Key.DisplayName).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
} }
// Teams
Model.MyTeamEmployees = EmployeeService.GetAllTeamMember(compactEmployee).OrderBy(employee => employee.LastName).ToList(); Model.MyTeamEmployees = EmployeeService.GetAllTeamMember(compactEmployee).OrderBy(employee => employee.LastName).ToList();
if(Model.HasRightToViewTeams) if(Model.HasRightToViewTeams)
@@ -143,6 +145,7 @@ namespace BeWoPlanerMobil.Controllers
} }
Model.TeamMemberCustomerOids = EmployeeService.LoadTeamsRelatedCustomerOids(employeeOid); Model.TeamMemberCustomerOids = EmployeeService.LoadTeamsRelatedCustomerOids(employeeOid);
// /Teams
Model.SelectedEmployeesForFiltering = Model.PossibleEmployees.Where(possibleEmployee => Model.SelectedEmployeeOidsForFiltering?.Contains(possibleEmployee.EmployeeOid) ?? false).ToList(); Model.SelectedEmployeesForFiltering = Model.PossibleEmployees.Where(possibleEmployee => Model.SelectedEmployeeOidsForFiltering?.Contains(possibleEmployee.EmployeeOid) ?? false).ToList();
Model.SelectedCustomersForFiltering = Model.PossibleCustomers.Where(possibleCustomer => Model.SelectedCustomerOidsForFiltering?.Contains(possibleCustomer.CustomerOid) ?? false).ToList(); Model.SelectedCustomersForFiltering = Model.PossibleCustomers.Where(possibleCustomer => Model.SelectedCustomerOidsForFiltering?.Contains(possibleCustomer.CustomerOid) ?? false).ToList();
@@ -171,7 +174,7 @@ namespace BeWoPlanerMobil.Controllers
var intervalStart = DateTimeExtensions.GetDayOfWeek(DayOfWeek.Monday); var intervalStart = DateTimeExtensions.GetDayOfWeek(DayOfWeek.Monday);
var intervalEnd = intervalStart.AddDays(7); var intervalEnd = intervalStart.AddDays(7);
var allAppointments = KalenderService.LoadFilteredAppointmentsMitAufgaben var allAppointments = KalenderService.LoadFilteredAppointmentsMitAufgabenForMoK
( (
Model.HasRightKalenderMitarbeitertermineAnsehen, Model.HasRightKalenderMitarbeitertermineAnsehen,
employeeOid, employeeOid,
@@ -278,24 +281,44 @@ namespace BeWoPlanerMobil.Controllers
if(Model.HasRightKalenderRessourcenTermineAndererAendern is false) if(Model.HasRightKalenderRessourcenTermineAndererAendern is false)
{ {
appointments appointments
.Where(appointment => appointment.Originator.Equals(Model.LoggedInCompactEmployee) is false && appointment.ResourceList.Any()) .Where(appointment =>
appointment.IsTask is false &&
appointment.IsAbsenceTime is false &&
appointment.Originator.Equals(Model.LoggedInCompactEmployee) is false && appointment.ResourceList.Any())
.DoForEach(appointment => appointment.CanBeEdited = false); .DoForEach(appointment => appointment.CanBeEdited = false);
} }
if(Model.HasRightKalenderKlientenTermineAendern is false) if(Model.HasRightKalenderKlientenTermineAendern is false)
{ {
appointments appointments
.Where(appointment => appointment.Originator.Equals(Model.LoggedInCompactEmployee) is false && appointment.CustomerList.Any(customer => relatedCustomerOids.Contains(customer.CustomerOid) is false)) .Where(appointment =>
appointment.IsTask is false &&
appointment.IsAbsenceTime is false &&
appointment.Originator.Equals(Model.LoggedInCompactEmployee) is false && appointment.CustomerList.Any())
.DoForEach(appointment => appointment.CanBeEdited = false); .DoForEach(appointment => appointment.CanBeEdited = false);
} }
if(Model.HasRightKalenderMitarbeiterTermineAendern is false) if(Model.HasRightKalenderMitarbeiterTermineAendern is false)
{ {
appointments appointments
.Where(appointment => appointment.Originator.Equals(Model.LoggedInCompactEmployee) is false) .Where(appointment =>
appointment.IsTask is false &&
appointment.IsAbsenceTime is false &&
appointment.Originator.Equals(Model.LoggedInCompactEmployee) is false)
.DoForEach(appointment => appointment.CanBeEdited = false); .DoForEach(appointment => appointment.CanBeEdited = false);
} }
foreach(var appointment in appointments.Where(apt => apt.IsTask is false && apt.IsAbsenceTime is false && apt.Originator.Equals(Model.LoggedInCompactEmployee) is false && apt.CustomerList.Any()))
{
var canEdit = appointment.CanBeEdited;
var canView = appointment.CanBeViewed;
var canDelete = appointment.CanBeDeleted;
var subject = appointment.Subject;
}
var x = appointments.Where(w => w.SchedulerAppointmentOid is null).ToList();
Model.Appointments = appointments; Model.Appointments = appointments;
LoadNotificationCount(); LoadNotificationCount();
@@ -1580,7 +1603,7 @@ namespace BeWoPlanerMobil.Controllers
} }
AppointmentModel.SelectedResourceOids = selectedOids.Length == 0 ? new List<long>() : selectedOids.Split(',').Select(long.Parse).ToList(); AppointmentModel.SelectedResourceOids = selectedOids.Length == 0 ? new List<long>() : selectedOids.Split(',').Select(long.Parse).ToList();
return JsonConvert.SerializeObject(AppointmentModel.SelectedResourceOids?.Count ?? 0); return JsonConvert.SerializeObject(AppointmentModel.SelectedResourceOids?.Count ?? 0);
} }
} }

View File

@@ -10,7 +10,6 @@ using BeWoPlanerMobil.Service;
using BeWoPlanerMobil.Util; using BeWoPlanerMobil.Util;
using BS.Shared; using BS.Shared;
using BS.Shared.DataContracts; using BS.Shared.DataContracts;
using DevExpress.XtraPrinting;
using HttpCookie = System.Web.HttpCookie; using HttpCookie = System.Web.HttpCookie;

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace BeWoPlanerMobil {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class DXLocalization {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal DXLocalization() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if(object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BeWoPlanerMobil.DXLocalization", typeof(DXLocalization).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ASPxSchedulerStringId.FloatingActionButton_NewAppointment" xml:space="preserve">
<value>Neuer Termin</value>
</data>
<data name="ASPxSchedulerStringId.ToolTip_DeleteAppointment" xml:space="preserve">
<value>Löschen</value>
</data>
<data name="ASPxSchedulerStringId.FloatingActionButton_DeleteAppointment" xml:space="preserve">
<value>Löschen</value>
</data>
<data name="SchedulerStringId.AppointmentLabel_Birthday" xml:space="preserve">
<value>Geburtstag</value>
</data>
<data name="SchedulerStringId.ViewShortDisplayName_Day" xml:space="preserve">
<value>Tag</value>
</data>
<data name="ASPxEditorsStringId.Calendar_Today" xml:space="preserve">
<value>Heute</value>
</data>
<data name="SchedulerStringId.ViewShortDisplayName_WorkDays" xml:space="preserve">
<value>Arbeitswoche</value>
</data>
</root>

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -9,6 +9,7 @@ using System.Web.Http;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Optimization; using System.Web.Optimization;
using System.Web.Routing; using System.Web.Routing;
using BeWoPlanerMobil.Util.SchedulerUtils;
namespace BeWoPlanerMobil namespace BeWoPlanerMobil
@@ -33,6 +34,12 @@ namespace BeWoPlanerMobil
BundleConfig.RegisterBundles(BundleTable.Bundles); BundleConfig.RegisterBundles(BundleTable.Bundles);
XmlConfigurator.Configure(); XmlConfigurator.Configure();
OwnSoftSchedulerLocalizer.Activate();
// Für den Localizer. Für Release wieder entfernen und durch die Zeile darunter ersetzen
//DevExpress.Utils.Localization.XtraLocalizer.EnableTraceSource("BeWoPlanerMobil");
//RELEASE: DevExpress.Utils.Localization.XtraLocalizer.UserResourceManager = DXLocalization.ResourceManager;
} }
protected void Application_BeginRequest(object sender, EventArgs e) protected void Application_BeginRequest(object sender, EventArgs e)
@@ -41,6 +48,9 @@ namespace BeWoPlanerMobil
// ToDo: Könnte Probleme beim Login machen, falls der den Header der Login-Anfrage benutzt. // ToDo: Könnte Probleme beim Login machen, falls der den Header der Login-Anfrage benutzt.
Response.Headers["Referrer-Policy"] = "no-referrer"; Response.Headers["Referrer-Policy"] = "no-referrer";
Thread.CurrentThread.CurrentCulture = _CultureInfo;
Thread.CurrentThread.CurrentUICulture = _CultureInfo;
} }
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e) protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)

View File

@@ -34,6 +34,8 @@ namespace BeWoPlanerMobil.Models
public bool HasServiceRecordEntry { get; set; } public bool HasServiceRecordEntry { get; set; }
public long? OriginatorOid { get; set; } public long? OriginatorOid { get; set; }
public int? RecurrenceIndex { get; set; }
public bool ShowEmployeeColors { get; set; } public bool ShowEmployeeColors { get; set; }
public List<ServiceRecordDC> ServiceRecordList { get; set; } public List<ServiceRecordDC> ServiceRecordList { get; set; }

View File

@@ -10,7 +10,20 @@ namespace BeWoPlanerMobil.Models
public List<long> SelectedResourceOids { get; set; } public List<long> SelectedResourceOids { get; set; }
public SchedulerViewType CurrentSchedulerViewType { get; set; } public SchedulerViewType CurrentSchedulerViewType { get; set; }
public List<long> SelectedEmployeeOidsForFiltering { get; set; } private List<long> _SelectedEmployeeOidsForFiltering;
public List<long> SelectedEmployeeOidsForFiltering
{
get
{
return _SelectedEmployeeOidsForFiltering;
}
set
{
_SelectedEmployeeOidsForFiltering = value;
}
}
public List<long> SelectedCustomerOidsForFiltering { get; set; } public List<long> SelectedCustomerOidsForFiltering { get; set; }
public List<long> SelectedResourceOidsForFiltering { get; set; } public List<long> SelectedResourceOidsForFiltering { get; set; }

View File

@@ -0,0 +1,44 @@
using System.Diagnostics;
using DevExpress.Utils.Localization.Internal;
using DevExpress.XtraScheduler.Localization;
namespace BeWoPlanerMobil.Util.SchedulerUtils
{
public class OwnSoftSchedulerLocalizer : SchedulerLocalizer
{
public override string GetLocalizedString(SchedulerStringId id)
{
switch(id)
{
case SchedulerStringId.ViewShortDisplayName_Day:
return "Tag";
case SchedulerStringId.ViewShortDisplayName_Week:
return "Woche";
case SchedulerStringId.TimeScaleDisplayName_Week:
return "Woche";
case SchedulerStringId.ViewShortDisplayName_WorkDays:
return "Arbeitswoche";
case SchedulerStringId.ViewDisplayName_WorkDays:
return "Arbeitswoche";
case SchedulerStringId.ViewShortDisplayName_Timeline:
return "Zeitleiste";
case SchedulerStringId.ViewDisplayName_Timeline:
return "Zeitleiste";
case SchedulerStringId.ViewDisplayName_Month:
return "Monat";
case SchedulerStringId.ViewShortDisplayName_Month:
return "Monat";
}
return base.GetLocalizedString(id);
}
public static void Activate()
{
var localizer = new OwnSoftSchedulerLocalizer();
var provider = new DefaultActiveLocalizerProvider<SchedulerStringId>(localizer);
SetActiveLocalizerProvider(provider);
}
}
}

View File

@@ -116,7 +116,7 @@
</div> </div>
<input class="form-control w-100" type="text" placeholder="Suchen..." oninput="employeeListSearchOnChange()" id="employee-search-input" /> <input class="form-control w-100" type="text" placeholder="Suchen..." oninput="employeeListSearchOnChange()" id="employee-search-input" />
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" onclick="resetEmployeeSearch()"> <button class="btn btn-sm btn-outline-secondary" type="button" onclick="resetEmployeeSearch()">
<span class="fas fa-times-circle"></span> <span class="fas fa-times-circle"></span>
</button> </button>
</div> </div>
@@ -137,7 +137,7 @@
<div class="card-body"> <div class="card-body">
@foreach(var team in Model.MyTeams) @foreach(var team in Model.MyTeams)
{ {
<button type="button" class="btn btn-bewo-teams w-100 my-2" onclick="selectTeamForAppointment(@team.TeamOid)">@team.Name</button> <button type="button" class="btn btn-sm btn-bewo-teams w-100 my-2" onclick="selectTeamForAppointment(@team.TeamOid)">@team.Name</button>
} }
</div> </div>
</div> </div>

View File

@@ -1,5 +1,4 @@
@using BeWoPlanerMobil.Util <script type="text/javascript">
<script type="text/javascript">
function onAppointmentDragTipDisplaying(s, e) { function onAppointmentDragTipDisplaying(s, e) {
const newIntervalText = e.toolTip.ConvertIntervalToString(e.data.GetInterval()); const newIntervalText = e.toolTip.ConvertIntervalToString(e.data.GetInterval());

View File

@@ -7,7 +7,8 @@
Html.EnableClientValidation(); Html.EnableClientValidation();
Html.EnableUnobtrusiveJavaScript(); Html.EnableUnobtrusiveJavaScript();
var isNew = Model.Oid is null; // Ist bei Serientermininstanzen ebenfalls null!
var isNew = Model.Oid is null && Model.RecurrenceIndex == 0 && Model.RecurrenceInfo is null;
var cancelButtonText = "Abbrechen"; var cancelButtonText = "Abbrechen";
@@ -84,6 +85,11 @@
var showResourceButton = (Model.HasRightKalenderRessourcentermineAnsehen && Model.Oid.HasValue) || var showResourceButton = (Model.HasRightKalenderRessourcentermineAnsehen && Model.Oid.HasValue) ||
(Model.Oid.HasValue && (Model.HasRightKalenderRessourcenTermineAendern && isOriginator || isOriginator is false && Model.HasRightKalenderRessourcenTermineAndererAendern)) || (Model.Oid.HasValue && (Model.HasRightKalenderRessourcenTermineAendern && isOriginator || isOriginator is false && Model.HasRightKalenderRessourcenTermineAndererAendern)) ||
(Model.Oid is null && Model.HasRightKalenderRessourcenTermineAnlegen); (Model.Oid is null && Model.HasRightKalenderRessourcenTermineAnlegen);
if(areButtonsVisible != Model.CanBeEdited)
{
}
} }
<style type="text/css"> <style type="text/css">
@@ -95,168 +101,154 @@
@using(Html.BeginForm()) @using(Html.BeginForm())
{ {
<table id="abc-table" class="w-100"> <div class="row" id="abc-table">
@* Betreff *@ <div class="col">
<tr> <div class="form-row">
<td colspan="2"> <div class="col-md">
<div class="input-group mb-2"> <div class="input-group mb-2">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Betreff</span> <span class="input-group-text">Betreff</span>
</div> </div>
<input class="form-control" type="text" @disabled id="Subject" name="Subject" value="@Model.Subject" /> <input class="form-control" type="text" @disabled id="Subject" name="Subject" value="@Model.Subject" />
</div> </div>
</td> </div>
</tr> </div>
<div class="form-row">
@* Ort *@ <div class="col-md">
<tr> <div class="input-group mb-2">
<td colspan="2"> <div class="input-group-prepend">
<div class="input-group mb-2"> <span class="input-group-text">Ort</span>
<div class="input-group-prepend">
<span class="input-group-text">Ort</span>
</div>
<input class="form-control" type="text" @disabled id="Location" name="Location" value="@Model.Location" />
</div>
</td>
</tr>
@* Start *@
<tr>
<td>
<div class="input-group mb-2">
<div class="input-group-prepend">
<span class="input-group-text">Start</span>
</div>
<input class="form-control" type="@dateInputType" @disabled id="StartDate" name="StartDate" value="@start" />
</div>
</td>
<td>
<div class="input-group pl-2 mb-2">
<div class="input-group-prepend">
<div class="input-group-text">
@Html.DevExpress().CheckBox(
settings =>
{
settings.Name = nameof(SchedulerAppointmentDC.AllDay);
settings.Width = Unit.Percentage(100);
settings.ReadOnly = disabled.Length > 0;
}).Bind(Model.AllDay).GetHtml()
</div>
</div>
<div class="input-group-append">
<span class="input-group-text">Ganztägig</span>
</div>
</div>
</td>
</tr>
@* Ende *@
<tr>
<td colspan="2">
<div class="input-group mb-2">
<div class="input-group-prepend">
<span class="input-group-text">Ende</span>
</div>
<input class="form-control" type="@dateInputType" @disabled id="EndDate" name="EndDate" value="@end" />
</div>
</td>
</tr>
@* Privat *@
<tr>
<td >
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">
@Html.DevExpress().CheckBox(
settings =>
{
settings.Name = nameof(SchedulerAppointmentDC.IsPrivate);
settings.Width = Unit.Percentage(100);
settings.Properties.RootStyle.CssClass = "";
settings.ReadOnly = disabled.Length > 0;
}).Bind(Model.IsPrivate).GetHtml()
</div> </div>
</div> <input class="form-control" type="text" @disabled id="Location" name="Location" value="@Model.Location" />
<div class="input-group-append">
<span class="input-group-text">Privat</span>
</div> </div>
</div> </div>
</td> </div>
</tr> <div class="form-row">
<div class="col-md">
<div class="input-group mb-2">
<div class="input-group-prepend">
<span class="input-group-text">Start</span>
</div>
<input class="form-control" type="@dateInputType" @disabled id="StartDate" name="StartDate" value="@start" />
</div>
</div>
</div>
<div class="form-row">
<div class="col-md">
<div class="input-group mb-2">
<div class="input-group-prepend">
<span class="input-group-text">Ende</span>
</div>
<input class="form-control" type="@dateInputType" @disabled id="EndDate" name="EndDate" value="@end" />
</div>
</div>
</div>
<div class="form-row">
<div class="col-md">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">
@Html.DevExpress().CheckBox(
settings =>
{
settings.Name = nameof(SchedulerAppointmentDC.IsPrivate);
settings.Width = Unit.Percentage(100);
settings.Properties.RootStyle.CssClass = "";
settings.ReadOnly = disabled.Length > 0;
}).Bind(Model.IsPrivate).GetHtml()
</div>
</div>
<div class="input-group-append">
<span class="input-group-text">Privat</span>
</div>
</div>
</div>
<div class="col-md">
<div class="input-group pl-2 mb-2 justify-content-end">
<div class="input-group-prepend">
<div class="input-group-text">
@Html.DevExpress().CheckBox(
settings =>
{
settings.Name = nameof(SchedulerAppointmentDC.AllDay);
settings.Width = Unit.Percentage(100);
settings.ReadOnly = disabled.Length > 0;
}).Bind(Model.AllDay).GetHtml()
</div>
</div>
<div class="input-group-append">
<span class="input-group-text">Ganztägig</span>
</div>
</div>
</div>
</div>
@if(Model.HasRightKalenderMitarbeitertermineAnsehen || Model.HasRightKalenderKliententermineAnsehen || Model.HasRightKalenderRessourcentermineAnsehen) @if(Model.HasRightKalenderMitarbeitertermineAnsehen || Model.HasRightKalenderKliententermineAnsehen || Model.HasRightKalenderRessourcentermineAnsehen)
{ {
<tr> <div class="form-row justify-content-center">
@* colspan="4" *@ @if(showEmployeeButton)
<td colspan="2"> {
<table class="w-100"> @Html.Partial("CustomEmployeeSelectPartial", Model)
<tr> }
<td style="width: 33%; padding: .25em;">
@if(showEmployeeButton)
{
@Html.Partial("CustomEmployeeSelectPartial", Model)
}
</td>
<td style="width: 33%; padding: .25em;">
@if(showCustomerButton)
{
@Html.Partial("CustomCustomerSelectPartial", Model)
}
</td>
<td style="width: 33%; padding: .25em;">
@if(showResourceButton)
{
@Html.Partial("CustomResourceSelectPartial", Model)
}
</td>
</tr>
</table>
</td>
</tr>
}
@* Notiz *@ @if(showCustomerButton)
<tr> {
<td colspan="2"> @Html.Partial("CustomCustomerSelectPartial", Model)
<div class="form-group"> }
<label for="Description">Notiz</label>
<textarea class="form-control" @disabled id="Description" name="Description">@Model.Description</textarea>
</div>
</td>
</tr>
</table>
@Html.DevExpress().AppointmentRecurrenceForm(ViewBag.AppointmentRecurrenceFormSettings).GetHtml() @if(showResourceButton)
{
@Html.Partial("CustomResourceSelectPartial", Model)
}
</div>
}
<table style="width: 100%"> <div class="form-row">
<tr> <div class="col-md">
@if(areButtonsVisible) <div class="form-group">
{ <label for="Description">Notiz</label>
<button type="button" class="btn btn-primary mr-2" id="Apply" name="Apply" onclick="OnAppointmentFormSave()">OK</button> <textarea class="form-control" @disabled id="Description" name="Description">@Model.Description</textarea>
} </div>
</div>
</div>
<div id="recurrence-container">
@{
var recurrenceSettings = (AppointmentRecurrenceFormSettings) ViewBag.AppointmentRecurrenceFormSettings;
<button type="button" class="btn btn-primary mr-2" id="Cancel" name="Cancel" onclick="OnAppointmentFormCancel()">@cancelButtonText</button> recurrenceSettings.Enabled = areButtonsVisible;
}
@if(isDeleteButtonVisible) @Html.DevExpress().AppointmentRecurrenceForm(recurrenceSettings).GetHtml()
{ </div>
<button type="button" class="btn btn-danger" id="Delete" name="Delete" onclick="OnAppointmentFormDelete()">Löschen</button>
} <div class="form-row">
</tr> <div class="col">
</table> <div class="float-right">
@if(areButtonsVisible)
{
<button type="button" class="btn btn-sm-auto btn-primary mr-2" id="Apply" name="Apply" onclick="OnAppointmentFormSave()">OK</button>
}
<table class="w-100"> <button type="button" class="btn btn-sm-auto btn-primary mr-2" id="Cancel" name="Cancel" onclick="OnAppointmentFormCancel()">@cancelButtonText</button>
<tr>
<td style="width: 100%;"> @if(isDeleteButtonVisible)
{
<button type="button" class="btn btn-sm-auto btn-danger" id="Delete" name="Delete" onclick="OnAppointmentFormDelete()">Löschen</button>
}
</div>
</div>
</div>
<div>
@Html.DevExpress().SchedulerStatusInfo( @Html.DevExpress().SchedulerStatusInfo(
settings => settings =>
{ {
settings.Name = "schedulerStatusInfo"; settings.Name = "schedulerStatusInfo";
settings.Priority = 1; settings.Priority = 1;
settings.SchedulerName = "scheduler"; settings.SchedulerName = "scheduler";
}).GetHtml() }).GetHtml()
</td> </div>
</tr> </div>
</table> </div>
} }

View File

@@ -12,9 +12,7 @@
const participationInfos = currentApt.ParticipationInfos; const participationInfos = currentApt.ParticipationInfos;
const resourceInfos = currentApt.ResourceInfos; const resourceInfos = currentApt.ResourceInfos;
const customerInfos = currentApt.CustomerInfos; const customerInfos = currentApt.CustomerInfos;
console.log(currentApt); // ToDo: Icon bei Serientermin! Index auch?
logInfo(`Mitarbeiter: ${participationInfos.length}\r\nKlienten: ${customerInfos.length}\r\nRessourcen: ${resourceInfos.length}`);
let bgClass = ""; let bgClass = "";
$("#tool-tip-info").empty(); $("#tool-tip-info").empty();
@@ -57,7 +55,6 @@
$("#resources-container").append(`<span class="small pr-1">Ressourcen:</span>`); $("#resources-container").append(`<span class="small pr-1">Ressourcen:</span>`);
resourceInfos.forEach((info) => { resourceInfos.forEach((info) => {
logInfo2(`Farbe der Ressource <<${info.ResourceName}>>: ${info.ResourceColor}`);
$("#resources-container").append(`<span class="badge badge-light border"><div class="mr-1" style="display:inline-block;border-radius:3px;vertical-align:middle;width:10px;height:10px;border:1px slategray;background-color: ${info.ResourceColor};"></div>${info.ResourceName}</span>`); $("#resources-container").append(`<span class="badge badge-light border"><div class="mr-1" style="display:inline-block;border-radius:3px;vertical-align:middle;width:10px;height:10px;border:1px slategray;background-color: ${info.ResourceColor};"></div>${info.ResourceName}</span>`);
}); });
} }
@@ -66,7 +63,7 @@
const textInterval = e.toolTip.ConvertIntervalToString(currentApt.interval); const textInterval = e.toolTip.ConvertIntervalToString(currentApt.interval);
$('#tool-tip-title').html(textInterval); $('#tool-tip-title').html(`${textInterval}: ${currentApt.GetSubject()}`);
if(currentApt.CanBeDeleted === false) { if(currentApt.CanBeDeleted === false) {
$("#aptToolTipDelete").hide(); $("#aptToolTipDelete").hide();
@@ -117,4 +114,12 @@
<div id="originator-container"></div> <div id="originator-container"></div>
</div> </div>
</div> </div>
<div class="card-footer m-0 p-1">
<button type="button" id="aptToolTipEdit" class="btn btn-sm btn-primary" onclick="onAptToolTipEditClick()">
Bearbeiten
</button>
<button type="button" id="aptToolTipDelete" class="btn btn-sm btn-danger float-right" onclick="onAptToolTipDeleteClick()">
Löschen
</button>
</div>
</div> </div>

View File

@@ -7,15 +7,15 @@
var allChecked = areAllSelected ? "checked" : string.Empty; var allChecked = areAllSelected ? "checked" : string.Empty;
var canEdit = Model.Oid.HasValue && Model.HasRightKalenderKlientenTermineAendern; var canEdit = Model.Oid.HasValue && Model.HasRightKalenderKlientenTermineAendern;
var canCreate = Model.Oid is null && Model.HasRightKalenderKlientenTermineAnlegen; var canCreate = Model.Oid is null && Model.RecurrenceIndex == 0 && Model.RecurrenceInfo is null && Model.HasRightKalenderKlientenTermineAnlegen;
var checkBoxEnabled = canEdit || canCreate; var checkBoxEnabled = canEdit || canCreate;
var disabled = checkBoxEnabled ? string.Empty : "disabled"; var disabled = checkBoxEnabled ? string.Empty : "disabled";
} }
<div class="dropdown w-100" id="customers-dropdown"> <div class="dropdown pr-1" id="customers-dropdown">
<button class="btn btn-bewo-customers dropdown-toggle w-100" type="button" data-toggle="dropdown"> <button class="btn btn-sm-auto btn-bewo-customers dropdown-toggle w-100" type="button" data-toggle="dropdown">
Klienten <span class="badge badge-light" id="apt-cus-badge">@Model.SelectedCustomerOids.Count</span> Klienten <span class="badge badge-light" id="apt-cus-badge">@Model.SelectedCustomerOids.Count</span>
</button> </button>
<div class="dropdown-menu" onclick="event.stopPropagation();"> <div class="dropdown-menu" onclick="event.stopPropagation();">
@@ -28,7 +28,7 @@
</div> </div>
<input type="text" class="form-control" @disabled id="cus-drop-search-input" placeholder="Suchen ..." oninput="onNewSearchInput('cus')"> <input type="text" class="form-control" @disabled id="cus-drop-search-input" placeholder="Suchen ..." oninput="onNewSearchInput('cus')">
<div class="input-group-append"> <div class="input-group-append">
<button type="button" class="btn btn-outline-secondary" @disabled onclick="resetNewSearch('cus')"> <button type="button" class="btn btn-sm-auto btn-outline-secondary" @disabled onclick="resetNewSearch('cus')">
<span class="fas fa-times-circle"></span> <span class="fas fa-times-circle"></span>
</button> </button>
</div> </div>

View File

@@ -37,15 +37,15 @@
var allChecked = areAllSelected ? "checked" : string.Empty; var allChecked = areAllSelected ? "checked" : string.Empty;
var isNew = Model.Oid is null; var isNew = Model.Oid is null && Model.RecurrenceIndex == 0 && Model.RecurrenceInfo is null;
var isEdit = isNew is false; var isEdit = isNew is false;
var checkBoxEnabled = isEdit && Model.HasRightKalenderMitarbeiterTermineAendern || isNew && Model.HasRightKalenderMitarbeiterTermineAnlegen; var checkBoxEnabled = isEdit && Model.HasRightKalenderMitarbeiterTermineAendern || isNew && Model.HasRightKalenderMitarbeiterTermineAnlegen;
var disabled = checkBoxEnabled is false || (Model.CanBeEdited is false && isNew is false) ? "disabled" : string.Empty; var disabled = checkBoxEnabled is false || (Model.CanBeEdited is false && isNew is false) ? "disabled" : string.Empty;
} }
<div class="dropdown w-100" id="employees-dropdown"> <div class="dropdown pr-1" id="employees-dropdown">
<button class="btn btn-bewo-employee dropdown-toggle w-100" type="button" data-toggle="dropdown"> <button class="btn btn-sm-auto btn-bewo-employee dropdown-toggle w-100" type="button" data-toggle="dropdown">
Mitarbeiter <span class="badge badge-light" id="apt-emp-badge">@Model.SelectedEmployee2SchedulerAppointments.Count</span> Mitarbeiter <span class="badge badge-light" id="apt-emp-badge">@Model.SelectedEmployee2SchedulerAppointments.Count</span>
</button> </button>
<div class="dropdown-menu" onclick="event.stopPropagation();"> <div class="dropdown-menu" onclick="event.stopPropagation();">
@@ -58,7 +58,7 @@
</div> </div>
<input type="text" class="form-control" @disabled id="emp-drop-search-input" placeholder="Suchen ..." oninput="onNewSearchInput('emp')"> <input type="text" class="form-control" @disabled id="emp-drop-search-input" placeholder="Suchen ..." oninput="onNewSearchInput('emp')">
<div class="input-group-append"> <div class="input-group-append">
<button type="button" class="btn btn-outline-secondary" @disabled onclick="resetNewSearch('emp')"> <button type="button" class="btn btn-sm-auto btn-outline-secondary" @disabled onclick="resetNewSearch('emp')">
<span class="fas fa-times-circle"></span> <span class="fas fa-times-circle"></span>
</button> </button>
</div> </div>

View File

@@ -21,7 +21,7 @@
var checkedValue = areAllSelected ? "checked" : string.Empty; var checkedValue = areAllSelected ? "checked" : string.Empty;
var isOriginator = Model.OriginatorOid.HasValue && Model.OriginatorOid.Value == Model.LoggedInCompactEmployee.EmployeeOid; var isOriginator = Model.OriginatorOid.HasValue && Model.OriginatorOid.Value == Model.LoggedInCompactEmployee.EmployeeOid;
var isNew = Model.Oid is null; var isNew = Model.Oid is null && Model.RecurrenceIndex == 0 && Model.RecurrenceInfo is null;
var canEditResApt = Model.HasRightKalenderRessourcenTermineAendern; var canEditResApt = Model.HasRightKalenderRessourcenTermineAendern;
var canEditOthersResApt = Model.HasRightKalenderRessourcenTermineAndererAendern; var canEditOthersResApt = Model.HasRightKalenderRessourcenTermineAndererAendern;
var canCreateResApt = Model.HasRightKalenderRessourcenTermineAnlegen; var canCreateResApt = Model.HasRightKalenderRessourcenTermineAnlegen;
@@ -33,8 +33,8 @@
var disabled = isDisabled ? "disabled" : string.Empty; var disabled = isDisabled ? "disabled" : string.Empty;
} }
<div class="dropdown w-100" id="resources-dropdown"> <div class="dropdown pr-1" id="resources-dropdown">
<button class="btn btn-bewo-resource dropdown-toggle w-100" type="button" data-toggle="dropdown"> <button class="btn btn-sm-auto btn-bewo-resource dropdown-toggle w-100" type="button" data-toggle="dropdown">
Ressourcen <span class="badge badge-light" id="apt-res-badge">@Model.SelectedResourceOids.Count</span> Ressourcen <span class="badge badge-light" id="apt-res-badge">@Model.SelectedResourceOids.Count</span>
</button> </button>
<div class="dropdown-menu" onclick="event.stopPropagation();"> <div class="dropdown-menu" onclick="event.stopPropagation();">
@@ -47,7 +47,7 @@
</div> </div>
<input type="text" class="form-control" id="res-drop-search-input" placeholder="Suchen ..." oninput="onNewResourceSeachInput()"> <input type="text" class="form-control" id="res-drop-search-input" placeholder="Suchen ..." oninput="onNewResourceSeachInput()">
<div class="input-group-append"> <div class="input-group-append">
<button type="button" class="btn btn-outline-secondary" onclick="resetNewResourceSearch()"> <button type="button" class="btn btn-sm-auto btn-outline-secondary" onclick="resetNewResourceSearch()">
<span class="fas fa-times-circle"></span> <span class="fas fa-times-circle"></span>
</button> </button>
</div> </div>
@@ -110,31 +110,4 @@
</nav> </nav>
</div> </div>
</div> </div>
</div> </div>
@* ToDo: Mit den Kategorien wie im alten Kalendermodul bauen!
@for(var i = 0; i < Model.PossibleResources.Count; i++)
{
var resource = Model.PossibleResources[i];
var isChecked = resource.ResourceOid.HasValue && Model.SelectedResourceOids.Contains(resource.ResourceOid.Value) ? "checked" : string.Empty;
var dNone = i / Model.ResourcePageLimit + 1 != 1 ? " d-none" : string.Empty;
<div class="dropdown-item w-100 @dNone" id="dropdown-item-@i">
<form class="form-inline justify-content-between">
<div class="form-group form-check">
<input class="form-check-input" type="checkbox" id="apt-edit-res-@resource.ResourceOid.Value" @isChecked onclick="updateEmployeeCustomerResourceSelection('res')"/>
<label for="apt-edit-res-@resource.ResourceOid.Value" class="form-check-label">
@resource.DetailDescription
</label>
</div>
@if(WebUtils.CheckColorString(resource.Color))
{
<div class="float-right" style="width: 10px; height: 10px; background-color: @resource.Color; border: 1px solid @resource.Color; border-radius: 3px;"></div>
}
</form>
</div>
}*@

View File

@@ -6,6 +6,15 @@
} }
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(() => {
const newAptBtn = $("#scheduler_fab").children().find(".dx-fab-context-text");
if(newAptBtn.length) {
newAptBtn.empty();
newAptBtn.append("<span>Neuer Termin</span>");
}
});
function UpdateSchedulerHeight() { function UpdateSchedulerHeight() {
scheduler.SetHeight(1); scheduler.SetHeight(1);
@@ -18,22 +27,44 @@
scheduler.SetHeight(containerHeight); scheduler.SetHeight(containerHeight);
} }
function getLabelByTextFromRecurrenceForm(labelText) {
return $("#appointmentRecurrenceForm_AptRecCtl_mainDiv").filter(function() {
return $(this).text().trim() === labelText;
});
}
function resizeSchedulerEditForm() { function resizeSchedulerEditForm() {
const editFormPopup = $("#abc-table").parent().parent().parent(); const editFormPopup = $("#abc-table").parent().parent().parent();
const vw = $(window).width(); const vw = $(window).width();
if(editFormPopup.css("top") !== undefined) {
editFormPopup.css("top", "50px");
const recurrence = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Recurrence')");
const daily = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Daily')");
const weekly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Weekly')");
const monthly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Monthly')");
const yearly = $("#appointmentRecurrenceForm_AptRecCtl_mainDiv label:contains('Yearly')");
recurrence.text("Serie");
daily.text("Täglich");
weekly.text("Wöchentlich");
monthly.text("Monatlich");
yearly.text("Jährlich");
}
if(editFormPopup.outerWidth() !== undefined) { if(editFormPopup.outerWidth() !== undefined) {
$.get("@Url.Action("GetEmployeePageCount")", function(editFormInfo) { $.get("@Url.Action("GetEmployeePageCount")", function(editFormInfo) {
pageCountCallback(editFormInfo, "emp"); pageCountCallback(editFormInfo, "emp");
}); });
$.get("@Url.Action("GetCustomerPageCount")", function(editFormInfo) { $.get("@Url.Action("GetCustomerPageCount")", function(editFormInfo) {
pageCountCallback(editFormInfo, "cus"); pageCountCallback(editFormInfo, "cus");
}); });
$.get("@Url.Action("GetResourcePageCount")", function(editFormInfo) { $.get("@Url.Action("GetResourcePageCount")", function(editFormInfo) {
pageCountCallback(editFormInfo, "res"); pageCountCallback(editFormInfo, "res");
}); });
} }
@@ -66,11 +97,11 @@
} }
} }
function pageCountCallback(editFormInfo, prefix) { function pageCountCallback(editFormInfo, prefix) {
const info = JSON.parse(editFormInfo); const info = JSON.parse(editFormInfo);
const items = $(`#dropdown-${prefix}-itm-container .dropdown-item`); const items = $(`#dropdown-${prefix}-itm-container .dropdown-item`);
switch(prefix) { switch(prefix) {
case "emp": case "emp":
aptEmpSelPageCount = info.PageCount; aptEmpSelPageCount = info.PageCount;
aptEmpSelPageLimit = info.PageLimit; aptEmpSelPageLimit = info.PageLimit;
@@ -88,7 +119,7 @@
break; break;
} }
window.buildPagination(1, 5, 1, prefix); window.buildPagination(1, 5, 1, prefix);
} }
function OnAppointmentFormSave(s, e) { function OnAppointmentFormSave(s, e) {
@@ -97,13 +128,13 @@
} }
} }
function OnAppointmentFormCancel(s, e) { function OnAppointmentFormCancel(s, e) {
scheduler.AppointmentFormCancel(); scheduler.AppointmentFormCancel();
} }
function OnAppointmentFormDelete(s, e) { function OnAppointmentFormDelete(s, e) {
scheduler.AppointmentFormDelete(); scheduler.AppointmentFormDelete();
} }
function IsValidAppointment() { function IsValidAppointment() {
$.validator.unobtrusive.parse("form"); $.validator.unobtrusive.parse("form");
@@ -249,25 +280,25 @@
} }
function performReload(shouldDoReloadNotifications) { function performReload(shouldDoReloadNotifications) {
if(shouldDoReloadNotifications === false) { if(shouldDoReloadNotifications === false) {
return; return;
} }
$("#scheduler-notification-container").load("@Url.Action("GetNotifications")"); $("#scheduler-notification-container").load("@Url.Action("GetNotifications")");
} }
function reloadScheduler(shouldDoReloadNotifications) { function reloadScheduler(shouldDoReloadNotifications) {
scheduler.PerformCallback({apptID: "", actionId: "Reload", args: null}, () => {performReload(shouldDoReloadNotifications); hideSpinner(); }); scheduler.PerformCallback({apptID: "", actionId: "Reload", args: null}, () => {performReload(shouldDoReloadNotifications); hideSpinner(); });
} }
@* CustomECRSelectionPartial *@ @* CustomECRSelectionPartial *@
function onNewSearchInput(prefix) { function onNewSearchInput(prefix) {
var searchText = $(`#${prefix}-drop-search-input`).val(); var searchText = $(`#${prefix}-drop-search-input`).val();
const menuItems = $(`#dropdown-${prefix}-itm-container .dropdown-item`); const menuItems = $(`#dropdown-${prefix}-itm-container .dropdown-item`);
if(searchText === null || searchText === undefined || searchText.length === 0) { if(searchText === null || searchText === undefined || searchText.length === 0) {
menuItems.show(); menuItems.show();
return; return;
} }
@@ -285,7 +316,7 @@
} }
function resetNewSearch(prefix) { function resetNewSearch(prefix) {
$(`#${prefix}-drop-search-input`).val(""); $(`#${prefix}-drop-search-input`).val("");
buildPagination(1, 5, 1, prefix); buildPagination(1, 5, 1, prefix);
showPage(1, prefix); showPage(1, prefix);
@@ -314,7 +345,7 @@
var aptResSelPageLimit = 0; var aptResSelPageLimit = 0;
function buildPagination(firstShownButton, lastShownButton, activeButton, prefix) { function buildPagination(firstShownButton, lastShownButton, activeButton, prefix) {
const pagination = $(`#apt-${prefix}-pagination`); const pagination = $(`#apt-${prefix}-pagination`);
pagination.empty(); pagination.empty();
var pageCount = 1; var pageCount = 1;
@@ -335,38 +366,34 @@
break; break;
} }
if(prefix === "res") {
logInfo2(`Es gibt ${pageCount} Seiten für die Ressourcen`);
}
if(pageCount <= 1) { if(pageCount <= 1) {
$(`#apt-${prefix}-pag-divider, #apt-${prefix}-pag-dropdow-item`).hide(); $(`#apt-${prefix}-pag-divider, #apt-${prefix}-pag-dropdow-item`).hide();
return; return;
} }
$(`#apt-${prefix}-pag-divider, #apt-${prefix}-pag-dropdow-item`).show(); $(`#apt-${prefix}-pag-divider, #apt-${prefix}-pag-dropdow-item`).show();
var limit = lastShownButton; var limit = lastShownButton;
if(pageCount < limit) { if(pageCount < limit) {
limit = pageCount; limit = pageCount;
} }
const firstPageLiClass = firstShownButton === 1 ? "page-item disabled" : "page-igem"; const firstPageLiClass = firstShownButton === 1 ? "page-item disabled" : "page-igem";
const lastPageLiClass = lastShownButton === pageCount ? "page-item disabled" : "page-igem"; const lastPageLiClass = lastShownButton === pageCount ? "page-item disabled" : "page-igem";
const prevPageLiClass = activeButton === 1 ? "page-item disabled" : "page-item"; const prevPageLiClass = activeButton === 1 ? "page-item disabled" : "page-item";
const nextPageLiClass = activeButton === pageCount ? "page-item disabled" : "page-igem"; const nextPageLiClass = activeButton === pageCount ? "page-item disabled" : "page-igem";
$(`<li class="${firstPageLiClass}" id="first-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${1}, '${prefix}')"><span class="fas fa-angle-double-left"></span></button></li>`).appendTo(pagination); $(`<li class="${firstPageLiClass}" id="first-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${1}, '${prefix}')"><span class="fas fa-angle-double-left"></span></button></li>`).appendTo(pagination);
$(`<li class="${prevPageLiClass}" id="prev-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${currentPage - 1}, '${prefix}')"><span class="fas fa-angle-left"></span></button></li>`).appendTo(pagination); $(`<li class="${prevPageLiClass}" id="prev-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${currentPage - 1}, '${prefix}')"><span class="fas fa-angle-left"></span></button></li>`).appendTo(pagination);
for(let i = firstShownButton; i <= limit; i++) { for(let i = firstShownButton; i <= limit; i++) {
$(`<li class="page-item ${(i === activeButton ? "active" : "")}"><button class="page-link" tabindex="-1" type="button" onclick="showPage(${i}, '${prefix}')">${i}</button></li>`).appendTo(pagination); $(`<li class="page-item ${(i === activeButton ? "active" : "")}"><button class="page-link" tabindex="-1" type="button" onclick="showPage(${i}, '${prefix}')">${i}</button></li>`).appendTo(pagination);
} }
$(`<li class="${nextPageLiClass}" id="next-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${currentPage + 1}, '${prefix}')"><span class="fas fa-angle-right"></span></button></li>`).appendTo(pagination); $(`<li class="${nextPageLiClass}" id="next-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${currentPage + 1}, '${prefix}')"><span class="fas fa-angle-right"></span></button></li>`).appendTo(pagination);
$(`<li class="${lastPageLiClass}" id="last-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${pageCount}, '${prefix}')"><span class="fas fa-angle-double-right"></span></button></li>`).appendTo(pagination); $(`<li class="${lastPageLiClass}" id="last-page-li"><button type="button" class="page-link" tabeindex="-1" onclick="showPage(${pageCount}, '${prefix}')"><span class="fas fa-angle-double-right"></span></button></li>`).appendTo(pagination);
} }
function showPage(page, prefix) { function showPage(page, prefix) {
@@ -388,8 +415,8 @@
break; break;
} }
const total = (items.length / 10) | 0; const total = (items.length / 10) | 0;
const currentPage = Math.max(1, Math.min(page, total)); const currentPage = Math.max(1, Math.min(page, total));
switch(prefix) { switch(prefix) {
case "emp": case "emp":
@@ -403,28 +430,28 @@
break; break;
} }
const start = (currentPage - 1) * 10; const start = (currentPage - 1) * 10;
const end = currentPage * 10; const end = currentPage * 10;
items.hide(); items.hide();
$.each(items.slice(start, end), function(index, item) { $.each(items.slice(start, end), function(index, item) {
$(item).removeClass("d-none"); $(item).removeClass("d-none");
$(item).show(); $(item).show();
}); });
const last = Math.min(pageCount, currentPage + 2); const last = Math.min(pageCount, currentPage + 2);
const first = Math.max(1, last - 4); const first = Math.max(1, last - 4);
buildPagination(first, last, currentPage, prefix); buildPagination(first, last, currentPage, prefix);
} }
function updateEmployeeCustomerResourceSelection(prefix) { function updateEmployeeCustomerResourceSelection(prefix) {
const checkedBoxes = $(`#dropdown-${prefix}-itm-container input[type=checkbox]:checked`); const checkedBoxes = $(`#dropdown-${prefix}-itm-container input[type=checkbox]:checked`);
var url = ""; var url = "";
switch(prefix) { switch(prefix) {
case "emp": case "emp":
url = "@Url.Action("UpdateAppointmentEmployeeSelection")"; url = "@Url.Action("UpdateAppointmentEmployeeSelection")";
break; break;
@@ -434,7 +461,7 @@
case "res": case "res":
url = "@Url.Action("UpdateAppointmentResourceSelection")"; url = "@Url.Action("UpdateAppointmentResourceSelection")";
break; break;
} }
var selectedOids = []; var selectedOids = [];
@@ -449,7 +476,7 @@
showSpinner(); showSpinner();
$.get( $.get(
url, url,
{ selectedOids: selectedOids.join(", ") }, { selectedOids: selectedOids.join(", ") },
function(elementCount) { function(elementCount) {
$(`#apt-${prefix}-badge`).text(elementCount); $(`#apt-${prefix}-badge`).text(elementCount);
@@ -459,28 +486,28 @@
function selectAllEmployeeCustomerResource(prefix) { function selectAllEmployeeCustomerResource(prefix) {
const isChecked = $(`#apt-${prefix}-select-all`).prop("checked"); const isChecked = $(`#apt-${prefix}-select-all`).prop("checked");
$(`#dropdown-${prefix}-itm-container input[type=checkbox]`).prop("checked", isChecked); $(`#dropdown-${prefix}-itm-container input[type=checkbox]`).prop("checked", isChecked);
updateEmployeeCustomerResourceSelection(prefix); updateEmployeeCustomerResourceSelection(prefix);
} }
function categoryCardHeaderClick(collapsibleId) { function categoryCardHeaderClick(collapsibleId) {
const collapsible = $(`#${collapsibleId}`); const collapsible = $(`#${collapsibleId}`);
if(collapsible.length === false) { if(collapsible.length === false) {
return; return;
} }
collapsible.collapse("toggle"); collapsible.collapse("toggle");
const e = window.event; const e = window.event;
e.cancelBubble = true; e.cancelBubble = true;
if(e.stopPropagation) { if(e.stopPropagation) {
e.stopPropagation(); e.stopPropagation();
} }
} }
function onNewResourceSeachInput() { function onNewResourceSeachInput() {
@@ -490,7 +517,7 @@
const collapse = $(`#dropdown-res-itm-container .dropdown-item .collapse`); const collapse = $(`#dropdown-res-itm-container .dropdown-item .collapse`);
if(searchText === null || searchText === undefined || searchText.length === 0) { if(searchText === null || searchText === undefined || searchText.length === 0) {
showAllResources(); showAllResources();
return; return;
} }
@@ -498,7 +525,7 @@
$.each(category2Resources, $.each(category2Resources,
function(index, item) { function(index, item) {
const categoryName = $(item).find("h6").text().toLowerCase(); const categoryName = $(item).find("h6").text().toLowerCase();
if(categoryName.includes(searchText)) { if(categoryName.includes(searchText)) {
$(item).show(); $(item).show();
@@ -521,20 +548,20 @@
); );
} }
function resetNewResourceSearch() { function resetNewResourceSearch() {
$(`#res-drop-search-input`).val(""); $(`#res-drop-search-input`).val("");
showAllResources(); showAllResources();
buildPagination(1, 5, 1, "res"); buildPagination(1, 5, 1, "res");
showPage(1, "res"); showPage(1, "res");
const e = window.event; const e = window.event;
e.cancelBubble = true; e.cancelBubble = true;
if(e.stopPropagation) { if(e.stopPropagation) {
e.stopPropagation(); e.stopPropagation();
} }
} }
function showAllResources() { function showAllResources() {
@@ -551,7 +578,7 @@
}); });
}); });
collapse.collapse("show"); collapse.collapse("show");
} }
@* /CustomECRSelectionPartial *@ @* /CustomECRSelectionPartial *@
@@ -562,9 +589,9 @@
@* ToolTip *@ @* ToolTip *@
var toolTipContext; var toolTipContext;
function onToolTipDisplaying(s, e) { function onToolTipDisplaying(s, e) {
toolTipContext = e; toolTipContext = e;
switch(e.toolTip.type) { switch(e.toolTip.type) {
case MVCxSchedulerToolTipType.Appointment: case MVCxSchedulerToolTipType.Appointment:
@@ -573,7 +600,7 @@
onAppointmentDragTipDisplaying(s, e); onAppointmentDragTipDisplaying(s, e);
break; break;
} }
} }
</script> </script>
<div class="dropdown"> <div class="dropdown">

View File

@@ -193,15 +193,9 @@
settings.CustomActionRouteValues = new { Controller = "DevExpressScheduler", Action = "CustomCallBackAction" }; settings.CustomActionRouteValues = new { Controller = "DevExpressScheduler", Action = "CustomCallBackAction" };
settings.ClientSideEvents.ToolTipDisplaying = "onToolTipDisplaying"; settings.ClientSideEvents.ToolTipDisplaying = "onToolTipDisplaying";
settings.OptionsToolTips.SetAppointmentToolTipTemplateContent(() => settings.OptionsToolTips.SetAppointmentToolTipTemplateContent(() => { Html.RenderPartial("CustomAppointmentToolTipPartial"); });
{
Html.RenderPartial("CustomAppointmentToolTipPartial");
});
settings.OptionsToolTips.SetAppointmentMobileToolTipTemplateContent(() => { Html.RenderPartial("CustomAppointmentToolTipPartial"); }); settings.OptionsToolTips.SetAppointmentMobileToolTipTemplateContent(() => { Html.RenderPartial("CustomAppointmentToolTipPartial"); });
settings.OptionsToolTips.SetAppointmentDragToolTipTemplateContent(() => settings.OptionsToolTips.SetAppointmentDragToolTipTemplateContent(() => { Html.RenderPartial("CustomAppointmentDragToolTip"); });
{
Html.RenderPartial("CustomAppointmentDragToolTip");
});
settings.OptionsCustomization.AllowInplaceEditor = UsedAppointmentType.None; settings.OptionsCustomization.AllowInplaceEditor = UsedAppointmentType.None;
settings.OptionsCustomization.AllowDisplayAppointmentFlyout = false; settings.OptionsCustomization.AllowDisplayAppointmentFlyout = false;
@@ -384,7 +378,9 @@
IsTask = container.Appointment.CustomFields["IsTask"] is true, IsTask = container.Appointment.CustomFields["IsTask"] is true,
DueDate = container.Appointment.CustomFields["DueDate"] is DateTime dueDate ? dueDate : null, DueDate = container.Appointment.CustomFields["DueDate"] is DateTime dueDate ? dueDate : null,
CompletedDate = container.Appointment.CustomFields["CompletedDate"] is DateTime completedDate ? completedDate : null, CompletedDate = container.Appointment.CustomFields["CompletedDate"] is DateTime completedDate ? completedDate : null,
OriginatorOid = (container.Appointment.CustomFields["Originator"] as CompactEmployeeDC)?.EmployeeOid OriginatorOid = (container.Appointment.CustomFields["Originator"] as CompactEmployeeDC)?.EmployeeOid,
RecurrenceInfo = container.Appointment.RecurrenceInfo?.ToXml(),
RecurrenceIndex = container.Appointment.RecurrenceIndex
}; };
var appointmentModel = ViewData["EditableAppointmentModel"] != null ? (AppointmentModel)ViewData["EditableAppointmentModel"] : newModel; var appointmentModel = ViewData["EditableAppointmentModel"] != null ? (AppointmentModel)ViewData["EditableAppointmentModel"] : newModel;

View File

@@ -275,7 +275,7 @@
var $target; var $target;
var targetIndex; var targetIndex;
// Prüfen ob die Tab-Ansicht sichtbar ist (mehrere Dokutypes vorhanden) // Prüfen, ob die Tab-Ansicht sichtbar ist (mehrere Dokutypes vorhanden)
var $activeTabTextarea = $('#dokufelderTab .tab-pane.active textarea'); var $activeTabTextarea = $('#dokufelderTab .tab-pane.active textarea');
if ($activeTabTextarea.length > 0) { if ($activeTabTextarea.length > 0) {
// Das aktive Tab-Textarea verwenden; Index aus der ID auslesen (sb-doku-textarea-{i}) // Das aktive Tab-Textarea verwenden; Index aus der ID auslesen (sb-doku-textarea-{i})

View File

@@ -32,7 +32,7 @@
oidString: selectedEmployeeOids.toString() oidString: selectedEmployeeOids.toString()
}, },
() => { () => {
$("#one-day-scheduler-partial-container").load('@Url.Action("FetchAppointments", "Scheduler")'); @*$("#one-day-scheduler-partial-container").load('@Url.Action("FetchAppointments", "Scheduler")');*@
} }
); );
} }

View File

@@ -81,6 +81,10 @@
<link type="text/css" rel="stylesheet" href="@Url.Content("~/Content/dx.common.css")" /> <link type="text/css" rel="stylesheet" href="@Url.Content("~/Content/dx.common.css")" />
<link type="text/css" rel="stylesheet" href="@Url.Content("~/Content/dx.light.css")" /> <link type="text/css" rel="stylesheet" href="@Url.Content("~/Content/dx.light.css")" />
<script>
DevExpress.localization.locale("de");
</script>
@* CSS für den neuen Kalender-Tooltip *@ @* CSS für den neuen Kalender-Tooltip *@
<style type="text/css"> <style type="text/css">
.toolTipPanel { .toolTipPanel {
@@ -406,16 +410,6 @@
<ul class="navbar-nav ml-auto"> <ul class="navbar-nav ml-auto">
@if (Html.IsInDebugMode()) @if (Html.IsInDebugMode())
{ {
using (Html.BeginForm("RedirectToDevExpressScheduler", "Main", FormMethod.Post))
{
<li class="nav-item">
<button class="btn btn-link text-light no-underline" onclick="showSpinner()">
<i class="far fa-calendar-alt text-bewo-calendar"></i>
DX Kalender
</button>
</li>
}
<li class="nav-item"> <li class="nav-item">
<button class="btn btn-link text-light no-underline" type="button" onclick="printLocalStorage()"> <button class="btn btn-link text-light no-underline" type="button" onclick="printLocalStorage()">
<i class="fab fa-firefox-browser text-bewo-dev"></i> <i class="fab fa-firefox-browser text-bewo-dev"></i>
@@ -433,6 +427,19 @@
</li> </li>
} }
} }
@if(Model.IsAllowedToSeeScheduler)
{
using (Html.BeginForm("RedirectToDevExpressScheduler", "Main", FormMethod.Post))
{
<li class="nav-item">
<button class="btn btn-link text-light no-underline" onclick="showSpinner()">
<i class="far fa-calendar-alt text-bewo-calendar"></i>
DX Kalender
</button>
</li>
}
}
@if (Model.IsAllowedToSeePersons) @if (Model.IsAllowedToSeePersons)
{ {

View File

@@ -1,7 +1,7 @@
INSERT INTO `.executedscripts` INSERT INTO `.executedscripts`
VALUES ('Changes_2026-05-12 AiPromptbausteinRoutine Favoriten', CURDATE()); VALUES ('Changes_2026-05-12 AiPromptbausteinRoutine Favoriten', CURDATE());
ALTER TABLE `aipromptbauteinroutine` ALTER TABLE `aipromptbausteinroutine`
RENAME TO `aipromptbausteinroutine` ; RENAME TO `aipromptbausteinroutine` ;
CREATE TABLE `aipromptbausteinroutine2empfavorite` ( CREATE TABLE `aipromptbausteinroutine2empfavorite` (

View File

@@ -272,5 +272,9 @@ namespace BeWo.Service.ServiceContracts
[FaultContract(typeof(BeWoFault))] [FaultContract(typeof(BeWoFault))]
[OperationContract] [OperationContract]
Dictionary<DateTimeSpan, Dictionary<DateTime, bool>> FindAppointmentsInRangeForIntervalFinder(int duration, DateTime startDate, DateTime endDate, List<long> resourceOids, List<long> customerOids, List<long> employeeOids, long loggedInEmployeeOid, int intervalBuffer = 30, bool skipWeekends = true); Dictionary<DateTimeSpan, Dictionary<DateTime, bool>> FindAppointmentsInRangeForIntervalFinder(int duration, DateTime startDate, DateTime endDate, List<long> resourceOids, List<long> customerOids, List<long> employeeOids, long loggedInEmployeeOid, int intervalBuffer = 30, bool skipWeekends = true);
[FaultContract(typeof(BeWoFault))]
[OperationContract]
List<SchedulerAppointmentDC> LoadFilteredAppointmentsMitAufgabenForMoK(bool pHasRightToSeeAllEmployeeAppointments, long pEmployeeOid, DateTime pIntervalStart, DateTime pIntervalEnd, List<long> pSelectedEmployees, List<long> pSelectedCustomer, List<long> pSelectedResources, bool pEmployeesOnly, bool pCustomersOnly, bool pResourcesOnly, bool pPrivateAppointmentsOnly, bool pOnlyMyAppointments, bool pShowTasks);
} }
} }

View File

@@ -1191,6 +1191,7 @@ namespace BeWo.Service.ServiceImplementations
ownerOid = pEmployeeOid; ownerOid = pEmployeeOid;
} }
// ToDo: Mit lokaler Variable arbeiten, weil sonst der angemeldete Mitarbeiter ignoriert wird!
if(!(!pHasRightToSeeAllEmployeeAppointments && pSelectedEmployees != null && pSelectedEmployees.Count == 1 && pSelectedEmployees.Contains(pEmployeeOid))) if(!(!pHasRightToSeeAllEmployeeAppointments && pSelectedEmployees != null && pSelectedEmployees.Count == 1 && pSelectedEmployees.Contains(pEmployeeOid)))
{ {
//Rausnehmen, sonst werden Termine nicht gezeigt, bei denen man Owner ist und kein Employee ausgewählt wurde. //Rausnehmen, sonst werden Termine nicht gezeigt, bei denen man Owner ist und kein Employee ausgewählt wurde.
@@ -2982,5 +2983,168 @@ namespace BeWo.Service.ServiceImplementations
throw Utils.CreateBeWoFaultException(e); throw Utils.CreateBeWoFaultException(e);
} }
} }
public List<SchedulerAppointmentDC> LoadFilteredAppointmentsMitAufgabenForMoK(bool pHasRightToSeeAllEmployeeAppointments, long pEmployeeOid, DateTime pIntervalStart, DateTime pIntervalEnd, List<long> pSelectedEmployees, List<long> pSelectedCustomer, List<long> pSelectedResources, bool pEmployeesOnly, bool pCustomersOnly, bool pResourcesOnly, bool pPrivateAppointmentsOnly, bool pOnlyMyAppointments, bool pShowTasks)
{
try
{
long? ownerOid = null;
var selectedEmployees = new List<long>(pSelectedEmployees ?? new List<long>());
//Keine Auswahl getroffen: Nur meine Termine anzeigen
if(selectedEmployees?.Count == 0 && pSelectedCustomer?.Count == 0 && pSelectedResources?.Count == 0)
{
ownerOid = pEmployeeOid;
}
//Benutzer hat sich selber selektiert
if((bool) selectedEmployees?.Exists(e => e == pEmployeeOid))
{
ownerOid = pEmployeeOid;
}
//Man hat nur Customer und/oder Ressourcen ausgewählt, dann dürfen die eigenen nicht angezeigt werden
if(selectedEmployees?.Count == 0 &&
(pSelectedCustomer?.Count > 0 ||
pSelectedResources?.Count > 0))
{
ownerOid = null;
}
//Wenn man kein Recht hat alle zu sehen, muss immer auf Owner gefiltert werden, außer es sind Ressourcen vorhanden. Dann wird anonymisiert.
if(!pHasRightToSeeAllEmployeeAppointments && pSelectedResources?.Count == 0)
{
ownerOid = pEmployeeOid;
}
// ToDo: Mit lokaler Variable arbeiten, weil sonst der angemeldete Mitarbeiter ignoriert wird!
if(!(!pHasRightToSeeAllEmployeeAppointments && selectedEmployees.Count == 1 && selectedEmployees.Contains(pEmployeeOid)))
{
//Rausnehmen, sonst werden Termine nicht gezeigt, bei denen man Owner ist und kein Employee ausgewählt wurde.
//Wird nicht rausgenommen, wenn man die Termine anderer Mitarbeiter nicht sehen darf und "nur meine Termine" ausgewählt hat.
//Sonst werden die Filter mit and und nicht mit or verknüpft.
selectedEmployees?.Remove(pEmployeeOid);
}
if(pPrivateAppointmentsOnly && selectedEmployees?.Count == 0 && pSelectedCustomer?.Count == 0 && pSelectedResources?.Count == 0 && selectedEmployees?.Count != 0 &&
(pSelectedCustomer?.Count > 0 || pSelectedResources?.Count > 0))
{
ownerOid = pEmployeeOid;
}
var appointments = DAOFactory.SearchDAO.LoadFilteredAppointmentsForEmployee(pHasRightToSeeAllEmployeeAppointments, ownerOid, pIntervalStart, pIntervalEnd, selectedEmployees, pSelectedCustomer, pSelectedResources, pEmployeesOnly, pCustomersOnly, pResourcesOnly, pPrivateAppointmentsOnly, pOnlyMyAppointments, false).ToList();
var serientermine = appointments.Where(app => app.RecurrenceInfo != null && app.Type == 1).ToList();
var all = MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(appointments).OrderBy(a => a.StartDate).ToList();
var filteredEmployeeOids = new List<long>();
if(ownerOid.HasValue)
{
filteredEmployeeOids.Add(ownerOid.Value);
}
foreach(var empOid in selectedEmployees)
{
filteredEmployeeOids.Add(empOid);
}
var result = FilterAppointments(all, pEmployeeOid, filteredEmployeeOids, pSelectedCustomer, pSelectedResources, pCustomersOnly, pResourcesOnly, pShowTasks);
//Check fehlerhafte RecurrenceInfos
foreach(var app in result)
{
if(!IsNullOrEmpty(app.RecurrenceInfo))
{
if(app.RecurrenceInfo.Contains("WeekDays=\"0\""))
{
app.RecurrenceInfo = app.RecurrenceInfo.Replace("WeekDays=\"0\"", "WeekDays=\"2\"");
}
}
}
var test = result.Count;
// Ausnahmen immer laden. Entsprechen die Ausnahmen nicht den Filterkriterien, werden sie als gelöscht markiert, um nicht auf dem Client angezeigt zu werden.
var ausnahmen = MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(DAOFactory.SearchDAO.FindAppointmentsByRecurrenecInfo(result.Where(w => w.Type == 1).Select(s => s.RecurrenceInfo).ToList(), true));
var demFilterEntsprechendeTermine = FilterAppointments(ausnahmen, pEmployeeOid, filteredEmployeeOids, pSelectedCustomer, pSelectedResources, pCustomersOnly, pResourcesOnly, pShowTasks);
foreach(var appointment in ausnahmen.Where(w => !demFilterEntsprechendeTermine.Contains(w)))
{
var kosmetisch = new SchedulerAppointmentDC
{
SchedulerAppointmentOid = appointment.SchedulerAppointmentOid * -1,
NewSchedulerAppointmentVersion = 1,
Type = 4,
RecurrenceInfo = appointment.RecurrenceInfo,
EmployeeList = appointment.EmployeeList,
CustomerList = appointment.CustomerList,
ResourceList = appointment.ResourceList,
Originator = appointment.Originator
};
result.Add(kosmetisch);
}
UserDC user;
if(LoggedInUserOperationContextExt.Current != null && LoggedInUserOperationContextExt.Current.User != null)
{
user = MapperFactory.UserDC_User.MapToNewDC(LoggedInUserOperationContextExt.Current.User);
}
else
{
user = MapperFactory.UserDC_User.MapToNewDC(SessionFacade.LoggedInUser);
}
var appointmentsToCheckForAnonymization = new List<SchedulerAppointmentDC>();
var relatedCustomerOids = DAOFactory.SearchDAO.FindTeamRelatedCustomerOids(user.Employee.EmployeeOid);
if(!(user is null))
{
// Bei ausgewählten Ressourcen oder auch "Nur Ressourcen" werden auch Termine mit den ausgewählten Ressourcen geladen und anonymisiert angezeigt, die der angemeldete Mitarbeiter nicht sehen darf.
// ToDo: Hat man das Rechte "Alles Ansehen", wird nicht anonymisiert, da CheckSchedulerRights true zurückgibt!
//var hasRightToSeeAll = user.HasRight(UserRightType.ViewAll);
//appointmentsToCheckForAnonymization =
// pSelectedResources.Any() || pResourcesOnly ?
// result.Where(w =>
// w.ResourceList.Any() && false == SUtils.CheckSchedulerRights(w, SchedulerRightsCheckType.View, user, relatedCustomerOids, true)).ToList() :
// new List<SchedulerAppointmentDC>();
AnonymizeAppointments(result.Where(w => false == SUtils.CheckSchedulerRights(w, SchedulerRightsCheckType.View, user, relatedCustomerOids, true)).ToList());
if(pSelectedResources?.Any() ?? false)
{
AnonymizeAppointments(result.Where(appointment => appointment.ResourceList.Any()).ToList());
}
// Es wird nach zugewiesenen Rechten gefiltert und ob die Termine bereits in der Liste mit den anonymisierten Terminen sind.
//result = result.Where(w => (!w.ResourceList.Any() || SUtils.CheckSchedulerRights(w, SchedulerRightsCheckType.View, user, relatedCustomerOids, true)) && !appointmentsToCheckForAnonymization.Any(a => a.SchedulerAppointmentOid == w.SchedulerAppointmentOid)).ToList();
//result.AddRange(appointmentsToCheckForAnonymization);
}
// Aufgaben laden
ConvertOldTasks(pEmployeeOid);
var taskAppointmentDCs = MapperFactory.SchedulerAppointmentDCSchedulerAppointment.MapToNewDCs(DAOFactory.SearchDAO.LoadTaskAppointmentsForEmployee(pEmployeeOid, true));
result.AddRangeIfElementsNotIn(taskAppointmentDCs);
//DebugUtils.WriteToLogFile($"{DateTime.Now:dd.MM.yyyy HH:mm:ss:fff}: {result.Count} Termine und {result.Count(w => w.IsTask)} Aufgaben aus der Datenbank geladen. {appointmentsToCheckForAnonymization.Count} davon anonymisiert.");
Debug.WriteLine($"+++>{DateTime.Now:dd.MM.yyyy HH:mm:ss:fff}: {result.Count} Termine und {result.Count(w => w.IsTask)} Aufgaben aus der Datenbank geladen. {appointmentsToCheckForAnonymization.Count} davon anonymisiert.");
// ToDo: durch jeden Termin gehen und prüfen, ob Klienten oder Mitarbeiter anonymisiert und auf read-only gesetzt werden müssen?
var appointmentsToBeAnonymized = result.Where(a => !SUtils.CheckSchedulerRights(a, SchedulerRightsCheckType.View, user, relatedCustomerOids, true)).ToList();
var rest = result.Except(appointmentsToBeAnonymized);
return result;
}
catch(Exception e)
{
throw Utils.CreateBeWoFaultException(e);
}
}
} }
} }

View File

@@ -65,7 +65,7 @@ namespace BeWoDatabaseUpdater
Console.WriteLine($"{DateTime.Now:dd.MM.yyyy HH:mm:ss.fff}: Führe Updates durch ...{Environment.NewLine}"); Console.WriteLine($"{DateTime.Now:dd.MM.yyyy HH:mm:ss.fff}: Führe Updates durch ...{Environment.NewLine}");
_BeWoSchemaNames = await MySqlBeWoHelper.LoadBeWoDatabaseNames(_ConnectionString) ?? []; _BeWoSchemaNames = ["3522103738"]; //await MySqlBeWoHelper.LoadBeWoDatabaseNames(_ConnectionString) ?? [];
LoadSqlScriptsFromFilesToMemory(() => LoadSqlScriptsFromFilesToMemory(() =>
{ {
@@ -74,6 +74,11 @@ namespace BeWoDatabaseUpdater
_FilePaths2Sql.DoForEach(path2Commands => _FilePaths2Sql.DoForEach(path2Commands =>
{ {
if(path2Commands is null)
{
return;
}
commandCount += path2Commands.Commands.Count; commandCount += path2Commands.Commands.Count;
fileCount++; fileCount++;
}); });
@@ -160,6 +165,8 @@ namespace BeWoDatabaseUpdater
//MySqlBeWoHelper.WriteSqlErrorsToFile(); //MySqlBeWoHelper.WriteSqlErrorsToFile();
MySqlBeWoHelper.DistinctErrors.DoForEach(number2Message => Console.WriteLine($"{number2Message.Key}: {number2Message.Value}")); MySqlBeWoHelper.DistinctErrors.DoForEach(number2Message => Console.WriteLine($"{number2Message.Key}: {number2Message.Value}"));
Console.WriteLine("Alle Updates durchgeführt!");
} }
private static void GetTextAndSqlFiles() private static void GetTextAndSqlFiles()