Associations

This commit is contained in:
Andy Howard
2024-12-04 15:30:59 +00:00
parent c8b5df5909
commit 30d0352f48
140 changed files with 5109 additions and 2879 deletions
@@ -609,4 +609,16 @@
<data name="label.reference.external" xml:space="preserve">
<value>Ext. reference</value>
</data>
<data name="label.location" xml:space="preserve">
<value>Location</value>
</data>
<data name="label.group" xml:space="preserve">
<value>Group</value>
</data>
<data name="caption.association.select" xml:space="preserve">
<value>Select Association</value>
</data>
<data name="caption.contact.select" xml:space="preserve">
<value>Select Contact</value>
</data>
</root>
@@ -47,7 +47,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App
SessionContext = sessionContext;
}
public SbmSessionContext_Web SessionContext { get; }
public HashSet<string> DbIdentifiers { get; } = new HashSet<string>();
public HashSet<string> RepositoryContexts { get; } = new HashSet<string>();
public ISacsDatabase BusMgrDatabase { get; set; }
public virtual SbmInstanceConfig InstanceConfig => null;
// Control database properties.
@@ -125,17 +125,25 @@ namespace Sarand.BusinessMgr.Web.Portal.App
{
return sessionContexts.Values.Select(item => item.SessionContext.Index).ToList();
}
public override SbmSessionContext GetSessionContext(ushort index)
public override SbmSessionContext GetSessionContextByIndex(ushort index)
{
return sessionContexts.GetValue(index, null)?.SessionContext;
}
public SbmSessionContext GetControlSessionContext(String dbIdentifier)
public override List<SbmSessionContext> GetSessionContexts()
{
return PopulateControlSessionContext(dbIdentifier)?.SessionContext;
return new List<SbmSessionContext>(sessionContexts.Values.Select(item => item.SessionContext));
}
public override HashSet<string> GetDbIdentifiers(SbmSessionContext sessionContext)
/// <summary>
/// Retrieve the Control Session Context that matches the Repository Context, if defined.
/// <para>- Control Session Context will be populated if not previously referenced.</para>
/// </summary>
public SbmSessionContext GetControlSessionContextByRepositoryContext(String repositoryContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new HashSet<string>();
return PopulateControlSessionContext(repositoryContext)?.SessionContext;
}
public override HashSet<string> GetRepositoryContexts(SbmSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.RepositoryContexts ?? new HashSet<string>();
}
// Databases.
public override ISacsDatabase GetControlDatabase()
@@ -240,7 +248,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App
ushort sessionContextIdx = sessionContextIdx_Next++;
SessionContextData sessionContextData = new SessionContextData_Core(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebPortal));
sessionContextData.BusMgrDatabase = coreDatabase;
sessionContextData.DbIdentifiers.UnionWith(dbConfig.Identifiers);
sessionContextData.RepositoryContexts.UnionWith(dbConfig.Identifiers);
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
}
else SaLogger.Warn($"{SysUtil.Fn} Database: " + coreDatabase.DatabaseName + ". Initialisation error: " + coreDatabase.ErrorStateMessage);
@@ -253,12 +261,12 @@ namespace Sarand.BusinessMgr.Web.Portal.App
}
//-------------------------------------------------------------------
private SessionContextData PopulateControlSessionContext(String dbIdentifier)
private SessionContextData PopulateControlSessionContext(String repositoryContext)
{
if (StringUtil.IsBlank(dbIdentifier)) return null;
if (StringUtil.IsBlank(repositoryContext)) return null;
// Check whether context already exists in cache.
SessionContextData sessionContextData = sessionContexts.Values.FirstOrDefault(sc => sc.IsControl && sc.DbIdentifiers.ContainsCI(dbIdentifier));
SessionContextData sessionContextData = sessionContexts.Values.FirstOrDefault(sc => sc.IsControl && sc.RepositoryContexts.ContainsCI(repositoryContext));
using (DisposableCollection<IDisposable> disposables = new DisposableCollection<IDisposable>())
{
SbmcInstanceMgr controlInstanceMgr = SbmGlobals_Web.System.CreateControlInstanceMgr();
@@ -267,7 +275,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App
// Check whether context is defined in Control database (and active).
SbmcRepository.Params prmRepository = new SbmcRepository.Params();
prmRepository.ContextsIncluded.Add(dbIdentifier);
prmRepository.ContextsIncluded.Add(repositoryContext);
SbmcRepository repository = controlInstanceMgr.GetRepository(prmRepository);
disposables.Add(repository);
@@ -301,14 +309,14 @@ namespace Sarand.BusinessMgr.Web.Portal.App
else SaLogger.Info($"{SysUtil.Fn} Control Instance refreshed: Context: {repository.Context}. Database: {repository.DatabaseName}.");
// Refresh properties.
sessionContextData.DbIdentifiers.Add(repository.Context);
sessionContextData.RepositoryContexts.Add(repository.Context);
sessionContextData.BusMgrDatabase = coreDatabase;
}
else SaLogger.Warn($"{SysUtil.Fn} Database: {coreDatabase.DatabaseName}. Initialisation error: {coreDatabase.ErrorStateMessage}");
}
else
{
SaLogger.Warn($"{SysUtil.Fn} Attempt to connect to inactive repository. Context: {dbIdentifier}.");
SaLogger.Warn($"{SysUtil.Fn} Attempt to connect to inactive repository. Context: {repositoryContext}.");
}
}
}
@@ -38,10 +38,10 @@ namespace Sarand.BusinessMgr.Web.Portal.App
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Organisation is optional.</para>
/// </summary>
public SbmSessionData_Portal(SbmSessionContext sessionContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
public SbmSessionData_Portal(SbmSessionContext sessionContext, String repositoryContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
base(sessionContext)
{
DbIdentifier = dbIdentifier;
RepositoryContext = repositoryContext;
securityProfile = new SbmSecurityProfile(this, false);
Organisation = new OrganisationData_Portal(associationOrganisation, affiliateOrganisation);
User = new UserData_Portal(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations);
@@ -100,7 +100,7 @@ namespace Sarand.BusinessMgr.Web.Portal.App
// Database context.
/// <summary>Database context specified on sign-in.</summary>
public String DbIdentifier { get; set; }
public String RepositoryContext { get; set; }
//-------------------------------------------------------------------
// Security Profile.
@@ -73,9 +73,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
}
data.Field.Value.Add($"{Model_Association.ItemField.Target}", affiliateTarget.GetDisplayName());
data.Field.Value.Add($"{Model_Association.ItemField.ReferenceExt}", association.RefExt);
data.Field.Value.Add($"{Model_Association.ItemField.Location}", association.Location);
data.Field.Value.Add($"{Model_Association.ItemField.Group}", association.Group);
data.Field.Value.Add($"{Model_Association.ItemField.DescDetail}", association.DescDetail);
data.Field.Value.Add($"{Model_Association.ItemField.Location}", association.Location);
data.Field.Value.Add($"{Model_Association.ItemField.Role}", association.Role);
data.Field.Value.Add($"{Model_Association.ItemField.PhoneMain}", association.PhoneMain);
data.Field.Value.Add($"{Model_Association.ItemField.Email}", association.Email);
@@ -83,7 +83,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
// Flags.
// Common properties.
data.StatusCode = association.StatusCode;
data.StatusDesc = association.StatusDesc;
data.Field.Value.Add($"{Model_Association.ItemField.Status}", association.StatusDesc);
// Cleanse field values.
data.Field.Value.RemoveAll(item => StringUtil.IsBlank(item.Value));
// Populate item list values that might be cleared during update after removing blank values to ensure that blank values are published.
@@ -99,6 +99,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
{
// Construction.
public AjaxResponse_AssociationConfig(SbmModel_Portal model) : base(model) { }
// Page caption.
public String Caption { get; set; }
// Configuration.
public Model_Association.ListConfigData Config { get; } = new Model_Association.ListConfigData();
public Model_Association.ItemConfigData ConfigCust { get; } = new Model_Association.ItemConfigData();
@@ -131,6 +133,14 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
}
private void PopulateAssociationConfig(Model_Association model, AjaxResponse_AssociationConfig responseData)
{
// Page caption.
Sbm.AssociationType associationType = model.GetFilterAssociationType();
if (associationType == Sbm.AssociationType.Customer)
responseData.Caption = Localiser["label.customers"];
else if (associationType == Sbm.AssociationType.Supplier)
responseData.Caption = Localiser["label.suppliers"];
responseData.Caption = StringUtil.OnBlank(responseData.Caption, Localiser["label.associations"]);
// Configuration.
model.PopulateListConfig(responseData.Config);
model.PopulateItemConfig(responseData.ConfigCust, null, SessionData_Portal.Organisation.AffiliateId, Sbm.AssociationType.Customer);
@@ -138,11 +148,11 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
// Reference data - types.
List<TypeCodeDesc> associationTypes = Internals.AssociationTypeCodes;
foreach (Sbm.AssociationType associationType in EnumUtil.GetValues<Sbm.AssociationType>())
foreach (Sbm.AssociationType type in EnumUtil.GetValues<Sbm.AssociationType>())
{
if (associationType <= 0 || associationType == Sbm.AssociationType.Internal) continue;
if (!SessionConsole.AllowAssociationType(associationType)) continue;
responseData.AssociationTypes.Add(associationTypes.GetCodeForType(associationType), Localiser.GetAssociationTypeCaption(associationType));
if (type <= 0 || type == Sbm.AssociationType.Internal) continue;
if (!SessionConsole.AllowAssociationType(type)) continue;
responseData.AssociationTypes.Add(associationTypes.GetCodeForType(type), Localiser.GetAssociationTypeCaption(type));
}
// Reference data - statuses.
@@ -158,6 +168,15 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
{
// Construction.
public AjaxResponse_AssociationList(SbmModel_Portal model) : base(model) { }
// View mode.
public class ModeData
{
public bool? Maintain { get; set; }
public bool? Search { get; set; }
public bool? Relationships { get; set; }
public bool? PriceSet { get; set; }
}
public ModeData Mode { get; set; }
// Association list.
public List<AssociationListData> Associations { get; } = new List<AssociationListData>();
}
@@ -178,13 +197,35 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
// Populate configuration data.
PopulateAssociationConfig(model, responseData);
// View mode.
responseData.Mode = new AjaxResponse_AssociationList.ModeData();
switch (model.GetFilterMode())
{
case Model_Association.AssociationViewMode.Search:
responseData.Mode.Search = true;
break;
case Model_Association.AssociationViewMode.Relationships:
responseData.Mode.Relationships = true;
break;
case Model_Association.AssociationViewMode.PriceSet:
responseData.Mode.PriceSet = true;
break;
default:
responseData.Mode.Maintain = true;
break;
}
// Item filter.
SbmAssociation.Params prmAssociation = new SbmAssociation.Params();
prmAssociation.AffiliateIdOwner = SessionData_Portal.Organisation.AffiliateId;
prmAssociation.Search = model.Filter.Search;
if (SysId.IsValid(StringUtil.ToSysId(model.Filter.OwnerId)))
prmAssociation.AffiliateIdOwner = StringUtil.ToSysId(model.Filter.OwnerId);
else prmAssociation.AffiliateIdOwner = SessionData_Portal.Organisation.AffiliateId;
if (SysId.IsValid(StringUtil.ToSysId(model.Filter.TargetId)))
prmAssociation.AffiliateIdTarget = StringUtil.ToSysId(model.Filter.TargetId);
prmAssociation.AssociationType = model.GetFilterAssociationType();
if (prmAssociation.AssociationType <= 0)
prmAssociation.AssociationType = Sbm.AssociationType.NotInternal;
prmAssociation.Search = model.Filter.Search;
prmAssociation.StatusCat = SbmAssociation.StatusCat.NotDeleted;
prmAssociation.SortColumns.Add(AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Target_Name));
model.Paging.ItemTotal = (ulong)AccountMgr.GetAssociationCount(prmAssociation);
@@ -15,6 +15,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Sarand.BusinessMgr.Web.Portal.Models.Contacts;
using Sarand.Net.Standard.Base.Core;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
{
@@ -33,7 +34,11 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index(Model_Association model)
[Route("Association")]
[Route("Associations")]
[Route("Customers")]
[Route("Suppliers")]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
@@ -46,7 +51,12 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
}
// Association page.
Model_Association model = new Model_Association(Localiser);
RefreshModel(model);
if (Request.Path.Value.MatchCI("/Customers/?$"))
model.Filter.AssociationType = Internals.AssociationTypeCodes.GetCodeForType(Sbm.AssociationType.Customer);
else if (Request.Path.Value.MatchCI("/Suppliers/?$"))
model.Filter.AssociationType = Internals.AssociationTypeCodes.GetCodeForType(Sbm.AssociationType.Supplier);
model.PopulateListConfig();
model.Paging.PageNo = SessionData_Portal.Profile.Associations.Filter.PageNo;
model.Paging.PageLength = SessionData_Portal.Profile.Associations.Filter.PageLength;
@@ -27,6 +27,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
// Affiliate data.
private class AffiliateListData : AjaxResponse_Data.ItemData_Base
{
// Affiliate properties with specific handling.
public String Name { get; set; }
// Affiliate properties for list with specific handling.
public long? AssociationCount { get; set; }
// Fields.
@@ -50,6 +52,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
private void PopulateAffiliateResponseItemData(Model_Contact model, SbmAffiliate affiliate, AffiliateItemData data)
{
PopulateAffiliateResponseListData(model, affiliate, data as AffiliateListData);
// Affiliate name - prominent property for selecting and editing.
data.Name = affiliate.Name;
// Affiliate type - populate for list ('Type' property populated with desc) and form (Type Code & Desc).
data.TypeCode = Internals.AffiliateTypeCodes.GetCodeForType(affiliate.AffiliateType);
data.TypeDesc = Internals.AffiliateTypeCodes.GetDescForType(affiliate.AffiliateType);
@@ -84,7 +88,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
data.Field.Value.Add($"{Model_Contact.ItemField.Prospect}", affiliate.Prospect ? "1" : "");
// Common properties.
data.StatusCode = affiliate.StatusCode;
data.StatusDesc = affiliate.StatusDesc;
data.Field.Value.Add($"{Model_Contact.ItemField.Status}", affiliate.StatusDesc);
// Cleanse field values.
data.Field.Value.RemoveAll(item => StringUtil.IsBlank(item.Value));
// Populate item list values that might be cleared during update after removing blank values to ensure that blank values are published.
@@ -172,6 +176,13 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
{
// Construction.
public AjaxResponse_ContactList(SbmModel_Portal model) : base(model) { }
// View mode.
public class ModeData
{
public bool? Maintain { get; set; }
public bool? Search { get; set; }
}
public ModeData Mode { get; set; }
// Affiliate list.
public List<AffiliateListData> Contacts { get; } = new List<AffiliateListData>();
}
@@ -192,10 +203,20 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
// Populate configuration data.
PopulateContactConfig(model, responseData);
// View mode.
responseData.Mode = new AjaxResponse_ContactList.ModeData();
Model_Contact.ContactViewMode viewMode = model.GetFilterMode();
if (viewMode == Model_Contact.ContactViewMode.Search)
responseData.Mode.Search = true;
else
responseData.Mode.Maintain = true;
// Item filter.
SbmAffiliate.Params prmAffiliate = new SbmAffiliate.Params();
prmAffiliate.Search = model.Filter.Search;
prmAffiliate.AffiliateType = model.GetFilterContactType();
if (prmAffiliate.AffiliateType == Sbm.AffiliateType.Any && viewMode == Model_Contact.ContactViewMode.Search)
prmAffiliate.AffiliateType = Sbm.AffiliateType.AnyContact;
prmAffiliate.StatusCat = SbmAffiliate.StatusCat.NotDeleted;
prmAffiliate.SortColumns.Add(AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Name));
prmAffiliate.SortColumns.Add(AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.LastName));
@@ -228,6 +249,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
columns.Remove(colAddress);
if (responseData.Config.Column.Hide.Contains($"{Model_Contact.ItemField.Email}"))
columns.Remove(colEmail);
if (responseData.Config.Column.Hide.Contains($"{Model_Contact.ItemField.Associations}"))
columns.Remove(colAssocCount);
// Sort Columns.
if (!prmAffiliate.SortColumns.Any())
@@ -243,9 +266,10 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts
AffiliateListData affiliateData = new AffiliateListData()
{
Id = values.GetString(colId.DataName),
Name = values.GetString(colName.DataName),
StatusCode = values.GetString(colStatus.DataName),
};
affiliateData.Field.Value.Add($"{Model_Contact.ItemField.Name}", values.GetString(colName.DataName));
affiliateData.Field.Value.Add($"{Model_Contact.ItemField.Name}", affiliateData.Name);
affiliateData.Field.Value.Add($"{Model_Contact.ItemField.Type}", Internals.AffiliateTypeCodes.GetDescForType(affiliateType));
affiliateData.Field.Value.Add($"{Model_Contact.ItemField.Address}", StringUtil.NullOnBlank(AccountMgr.GetAffiliateDataValue(colAddress, values).AsString()));
affiliateData.Field.Value.Add($"{Model_Contact.ItemField.Contact}", StringUtil.NullOnBlank(values.GetString(colContact.DataName)));
@@ -23,6 +23,7 @@ using System.Web;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Web.Portal.Helpers;
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
@@ -79,7 +80,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
/// <para>- Open user session - store details against Sesion Data.</para>
/// <para>- Set 'Keep logged in' cookie if required.</para>
/// </summary>
protected bool SignUserIn(ISbmUserContext_Core userContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
protected bool SignUserIn(ISbmUserContext_Core userContext, String repositoryContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
{
// Validate Security Item and Affiliate, just in case - should have been done before arriving here!
if (securityItemUser == null)
@@ -150,7 +151,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
}
// Create session.
OpenUserSession(userContext.SessionContext, dbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
OpenUserSession(userContext.SessionContext, repositoryContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
// Configure security profile.
SessionData_Portal.SecurityProfile_Core.RefreshProfile(SessionConsole, SecurityConsole, securityItemUser.Id, SecurityConsole.GetRegisteredRights());
@@ -180,7 +181,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
//-------------------------------------------------------------------
/// <summary>Start a new user session on sign-in.</summary>
protected bool OpenUserSession(SbmSessionContext sessionContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
protected bool OpenUserSession(SbmSessionContext sessionContext, String repositoryContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
// Validate user properties.
if (!SysId.IsValid(affiliateUser.Id)) return false;
@@ -190,7 +191,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
CloseUserSession();
// Open session.
return SetSessionData(new SbmSessionData_Portal(sessionContext, dbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation));
return SetSessionData(new SbmSessionData_Portal(sessionContext, repositoryContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation));
}
protected void CloseUserSession()
{
@@ -240,45 +241,48 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
// User may be defined on any one of the BusinessMgr databases - need to find out which.
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(keepLoggedInParams.DbIdentifier, keepLoggedInParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
List<SbmSessionContext> sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(keepLoggedInParams.RepositoryContext, keepLoggedInParams.EmailAddress);
if (sessionContexts.Any())
{
// Consoles and Managers must be associated with the specific context.
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext))
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
foreach (SbmSessionContext sessionContext in sessionContexts)
{
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateIdUser))
using (SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationById(keepLoggedInParams.AssociationIdOrganisation))
using (SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null))
// Consoles and Managers must be associated with the specific context.
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext))
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
{
if (securityItemUser != null && affiliateUser != null && associationOrganisation != null && affiliateOrganisation != null)
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateIdUser))
using (SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationById(keepLoggedInParams.AssociationIdOrganisation))
using (SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null))
{
if (securityItemUser.Id.Equals(keepLoggedInParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id))
if (securityItemUser != null && affiliateUser != null && associationOrganisation != null && affiliateOrganisation != null)
{
if (affiliateUser.IsStatusCurrent)
if (securityItemUser.Id.Equals(keepLoggedInParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id))
{
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
String errorMessage = "";
if (SignUserIn(userContext, keepLoggedInParams.DbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage))
if (affiliateUser.IsStatusCurrent)
{
SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix);
return true;
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
String errorMessage = "";
if (SignUserIn(userContext, keepLoggedInParams.RepositoryContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage))
{
SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix);
return true;
}
else SaLogger.Warn(logPrefix + "failed to sign user in: " + errorMessage + "." + logSuffix);
}
else SaLogger.Warn(logPrefix + "failed to sign user in: " + errorMessage + "." + logSuffix);
else SaLogger.Warn(logPrefix + "Affiliate is not active." + logSuffix);
}
else SaLogger.Warn(logPrefix + "Affiliate is not active." + logSuffix);
else SaLogger.Warn(logPrefix + "Security Item and Affiliate are inconsistent. " + logSuffix);
}
else SaLogger.Warn(logPrefix + "Security Item and Affiliate are inconsistent. " + logSuffix);
else SaLogger.Warn(logPrefix + "Security Item or Affiliate not found. " + logSuffix);
}
else SaLogger.Warn(logPrefix + "Security Item or Affiliate not found. " + logSuffix);
}
if (ValidUserSession)
break;
}
if (ValidUserSession)
break;
}
else SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but no matching session contexts found." + logSuffix);
}
else if (!StringUtil.IsEmpty(encryptedParams))
SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but with incomplete details." + logSuffix);
@@ -322,7 +326,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
//-------------------------------------------------------------------
// Keep-logged-in cookie value.
private static readonly String ParamName_KeepLoggedIn_DbIdentifier = "dbi";
private static readonly String ParamName_KeepLoggedIn_RepositoryContext = "rct";
private static readonly String ParamName_KeepLoggedIn_EmailAddress = "ema";
private static readonly String ParamName_KeepLoggedIn_SecurityItemId = "sii";
private static readonly String ParamName_KeepLoggedIn_AffiliateIdUser = "afiu";
@@ -330,7 +334,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
private String GenerateKeepLoggedInCookieValue()
{
NameValueCollection keepLoggedInParams = new NameValueCollection();
keepLoggedInParams.Add(ParamName_KeepLoggedIn_DbIdentifier, SessionData_Portal.DbIdentifier);
keepLoggedInParams.Add(ParamName_KeepLoggedIn_RepositoryContext, SessionData_Portal.RepositoryContext);
keepLoggedInParams.Add(ParamName_KeepLoggedIn_EmailAddress, SessionData_Portal.User.EmailAddress);
keepLoggedInParams.Add(ParamName_KeepLoggedIn_SecurityItemId, SysId.ToString(SessionData_Portal.User.SecurityItemId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AffiliateIdUser, SysId.ToString(SessionData_Portal.User.AffiliateId));
@@ -341,7 +345,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
//-------------------------------------------------------------------
public class KeepLoggedInParams
{
public String DbIdentifier { get; set; }
public String RepositoryContext { get; set; }
public String EmailAddress { get; set; }
public SysId SecurityItemId { get; set; }
public SysId AffiliateIdUser { get; set; }
@@ -350,8 +354,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
public List<String> GetValidParams()
{
List<String> values = new List<String>();
if (!StringUtil.IsBlank(DbIdentifier))
values.Add("DbIdentifier: " + DbIdentifier);
if (!StringUtil.IsBlank(RepositoryContext))
values.Add("RepositoryContext: " + RepositoryContext);
if (!StringUtil.IsBlank(EmailAddress))
values.Add("EmailAddress: " + EmailAddress);
if (SysId.IsValid(SecurityItemId))
@@ -366,7 +370,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
public bool IsValid()
{
return
!StringUtil.IsBlank(DbIdentifier) &&
!StringUtil.IsBlank(RepositoryContext) &&
!StringUtil.IsBlank(EmailAddress) &&
SysId.IsValid(SecurityItemId) &&
SysId.IsValid(AffiliateIdUser) &&
@@ -379,7 +383,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, KeepLoggedIn.Base64IV, KeepLoggedIn.Base64Key);
keepLoggedInParams.DbIdentifier = linkParams.Get(ParamName_KeepLoggedIn_DbIdentifier);
keepLoggedInParams.RepositoryContext = linkParams.Get(ParamName_KeepLoggedIn_RepositoryContext);
keepLoggedInParams.EmailAddress = linkParams.Get(ParamName_KeepLoggedIn_EmailAddress);
keepLoggedInParams.SecurityItemId = linkParams.Get(ParamName_KeepLoggedIn_SecurityItemId).ToSysId();
keepLoggedInParams.AffiliateIdUser = linkParams.Get(ParamName_KeepLoggedIn_AffiliateIdUser).ToSysId();
@@ -67,7 +67,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
if (!(SessionData_Portal?.ValidUser ?? false))
{
// Instance Repository configured on Control database.
SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContext(model.DbIdentifier);
SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContextByRepositoryContext(model.RepositoryContext);
String errorMessage = "";
if (!OpenUserSession(model, sessionContext, ref errorMessage))
@@ -79,15 +79,13 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
if (!(SessionData_Portal?.ValidUser ?? false))
{
// AppSettings configured session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(model.DbIdentifier, model.EmailAddress);
if (sessionContextIdxs.IsEmpty())
// Session contexts with a BusMgr database that hosts the specified email address.
List<SbmSessionContext> sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(model.RepositoryContext, model.EmailAddress);
if (sessionContexts.IsEmpty())
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
foreach (ushort sessionContextIdx in sessionContextIdxs)
foreach (SbmSessionContext sessionContext in sessionContexts)
{
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
String errorMessage = "";
if (OpenUserSession(model, sessionContext, ref errorMessage)) break;
if (!StringUtil.IsBlank(errorMessage))
@@ -99,7 +97,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
if (!(SessionData_Portal?.ValidUser ?? false))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to identify Suitable database. Context: {model.DbIdentifier}. Email: {model.EmailAddress}.");
SaLogger.Warn($"{SysUtil.Fn} Failed to identify Suitable database. Context: {model.RepositoryContext}. Email: {model.EmailAddress}.");
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
}
@@ -177,7 +175,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
}
// Initialise user session.
if (SignUserIn(userContext, model.DbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, model.KeepMeSignedIn, ref errorMessage))
if (SignUserIn(userContext, model.RepositoryContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, model.KeepMeSignedIn, ref errorMessage))
return true;
}
return false;
@@ -409,42 +407,41 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
SbmAffiliate affiliateUser = null;
bool validRequestPasswordParams = false;
bool validSessionRequired = true;
RequestPasswordParams requestPasswordParams = ExtractRequestPasswordLinkParams(model.rpe);
if (requestPasswordParams.IsValid())
RequestPasswordParams storePasswordParams = ExtractRequestPasswordLinkParams(model.rpe);
if (storePasswordParams.IsValid())
{
// User may be defined on any one of the BusinessMgr databases - need to find out which.
// Identify session contexts for databases with specified Id that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(requestPasswordParams.DbIdentifier, requestPasswordParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
List<SbmSessionContext> sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(storePasswordParams.RepositoryContext, storePasswordParams.EmailAddress);
foreach (SbmSessionContext sessionContext in sessionContexts)
{
// Consoles and Managers must be associated with the specific context.
sessionContextForUser = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
userContextForUser = new SbmUserContext_Web(sessionContextForUser);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContextForUser))
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContextForUser))
{
// Retrieve user details and validate email address.
securityItemUser = securityConsoleForUser.GetSecurityItemById(SaSecurity.ItemType.User, requestPasswordParams.SecurityItemId);
affiliateUser = accountMgrForUser.GetAffiliateById(requestPasswordParams.AffiliateId);
if (requestPasswordParams.IsValid(securityItemUser, affiliateUser))
securityItemUser = securityConsoleForUser.GetSecurityItemById(SaSecurity.ItemType.User, storePasswordParams.SecurityItemId);
affiliateUser = accountMgrForUser.GetAffiliateById(storePasswordParams.AffiliateId);
if (storePasswordParams.IsValid(securityItemUser, affiliateUser))
{
sessionContextForUser = sessionContext;
userContextForUser = userContext;
validRequestPasswordParams = true;
validSessionRequired = false;
break;
}
else
{
SaLogger.Warn($"{SysUtil.Fn} 'Request Password' parameters supplied but invalid. Email: {requestPasswordParams.EmailAddress}. SecurityItemId: {SysId.ToString(requestPasswordParams.SecurityItemId)}. AffiliateId: {SysId.ToString(requestPasswordParams.AffiliateId)}.");
SaLogger.Warn($"{SysUtil.Fn} 'Request Password' parameters supplied but invalid. Email: {storePasswordParams.EmailAddress}. SecurityItemId: {SysId.ToString(storePasswordParams.SecurityItemId)}. AffiliateId: {SysId.ToString(storePasswordParams.AffiliateId)}.");
if (securityItemUser != null) securityItemUser.Dispose(); securityItemUser = null;
if (affiliateUser != null) affiliateUser.Dispose(); affiliateUser = null;
sessionContextForUser = null;
userContextForUser = null;
}
}
}
}
// User must have a valid session if security item and affiliate not idenfied via the 'Request Password' paramaters.
// User must have a valid session if security item and affiliate not idenfied via the 'Request Password' parameters.
if (validSessionRequired && (securityItemUser == null || affiliateUser == null) && !ValidUserSession)
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
@@ -541,10 +538,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
if (responseData.HasControlErrors()) return Json(responseData);
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(model.DbIdentifier, model.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
List<SbmSessionContext> sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(model.RepositoryContext, model.EmailAddress);
foreach (SbmSessionContext sessionContext in sessionContexts)
{
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
// Get user security item and affiliate.
@@ -600,14 +596,14 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
emailConsole.ReplaceDynamicEmailBodyTags(emailData, null);
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(SessionData_Portal.DbIdentifier, securityItemUser.Id, affiliateUser.Id, securityItemUser.EmailAddress, ""));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(SessionData_Portal.RepositoryContext, securityItemUser.Id, affiliateUser.Id, securityItemUser.EmailAddress, ""));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", GetRequestPasswordEmailLinkExpiresMinutes().ToString());
}
//-------------------------------------------------------------------
// Password request link.
private static String ParamName_RequestPasswordEncrypted { get; } = "rpe";
private static String ParamName_DbIdentifier { get; } = "dbi";
private static String ParamName_RepositoryContext { get; } = "rct";
private static String ParamName_EmailAddress { get; } = "ema";
private static String ParamName_SecurityItemId { get; } = "sei";
private static String ParamName_AffiliateId { get; } = "afi";
@@ -615,10 +611,10 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
private static String ParamName_Token { get; } = "tok";
// Portal Request Password encryption parameters (key & IV) are defined in the base Controller
// so that they can be shared with the Console application.
private String GenerateRequestPasswordLink(String dbIdentifier, SysId securityItemId, SysId affiliateId, String emailAddress, String requestPasswordToken)
private String GenerateRequestPasswordLink(String repositoryContext, SysId securityItemId, SysId affiliateId, String emailAddress, String requestPasswordToken)
{
NameValueCollection parameters = new NameValueCollection();
parameters.Add(ParamName_DbIdentifier, dbIdentifier);
parameters.Add(ParamName_RepositoryContext, repositoryContext);
parameters.Add(ParamName_EmailAddress, emailAddress);
parameters.Add(ParamName_SecurityItemId, SysId.ToString(securityItemId));
parameters.Add(ParamName_AffiliateId, SysId.ToString(affiliateId));
@@ -638,7 +634,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
}
private class RequestPasswordParams
{
public String DbIdentifier { get; set; }
public String RepositoryContext { get; set; }
public String EmailAddress { get; set; }
public SysId SecurityItemId { get; set; }
public SysId AffiliateId { get; set; }
@@ -648,8 +644,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
public List<String> GetValidParams()
{
List<String> values = new List<String>();
if (!StringUtil.IsBlank(DbIdentifier))
values.Add($"DbIdentifier: {DbIdentifier}");
if (!StringUtil.IsBlank(RepositoryContext))
values.Add($"RepositoryContext: {RepositoryContext}");
if (!StringUtil.IsBlank(EmailAddress))
values.Add($"Email: {EmailAddress}");
if (SysId.IsValid(SecurityItemId))
@@ -666,7 +662,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
public bool IsValid()
{
return
!StringUtil.IsBlank(DbIdentifier) &&
!StringUtil.IsBlank(RepositoryContext) &&
StringUtil.IsBlank(EmailAddress) &&
SysId.IsValid(SecurityItemId) &&
SysId.IsValid(AffiliateId) &&
@@ -690,7 +686,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword_Portal, Base64Key_RequestPassword_Portal);
requestPasswordParams.DbIdentifier = linkParams.Get(ParamName_DbIdentifier);
requestPasswordParams.RepositoryContext = linkParams.Get(ParamName_RepositoryContext);
requestPasswordParams.EmailAddress = linkParams.Get(ParamName_EmailAddress);
requestPasswordParams.SecurityItemId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_SecurityItemId)));
requestPasswordParams.AffiliateId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_AffiliateId)));
@@ -16,6 +16,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Sarand.BusinessMgr.Web.Portal.Helpers;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
{
@@ -70,32 +71,31 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security
// Check for encrypted parameters from 'Request Password' email link.
// NB Only allowed as a 'GET' request.
String encryptedParams = Request.Query[ParamName_RequestPasswordEncrypted];
RequestPasswordParams requestPasswordParams = ExtractRequestPasswordLinkParams(encryptedParams);
if (requestPasswordParams.IsValid())
RequestPasswordParams changePasswordParams = ExtractRequestPasswordLinkParams(encryptedParams);
if (changePasswordParams.IsValid())
{
String logPrefix = $"{SysUtil.Fn} 'Request Password' parameters supplied but ";
String logSuffix = requestPasswordParams.GetValidParams().Concat("", " ", ".");
String logSuffix = changePasswordParams.GetValidParams().Concat("", " ", ".");
// User may be defined on any one of the BusinessMgr databases - need to find out which.
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(requestPasswordParams.DbIdentifier, requestPasswordParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
List<SbmSessionContext> sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(changePasswordParams.RepositoryContext, changePasswordParams.EmailAddress);
foreach (SbmSessionContext sessionContext in sessionContexts)
{
// Consoles and Managers must be associated with the specific context.
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext))
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
{
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(requestPasswordParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(requestPasswordParams.AffiliateId))
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(changePasswordParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(changePasswordParams.AffiliateId))
{
if (securityItemUser != null && affiliateUser != null)
{
if (securityItemUser.Id.Equals(requestPasswordParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id))
if (securityItemUser.Id.Equals(changePasswordParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id))
{
if (affiliateUser.IsStatusCurrent)
{
if (requestPasswordParams.IsValid(securityItemUser, affiliateUser))
if (changePasswordParams.IsValid(securityItemUser, affiliateUser))
{
model.rpe = encryptedParams;
validSessionRequired = false;
@@ -1,8 +1,15 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Web.Portal.App;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Dbas.Data;
using System;
using System.Collections.Generic;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
namespace Sarand.BusinessMgr.Web.Portal.Helpers
{
@@ -12,12 +19,104 @@ namespace Sarand.BusinessMgr.Web.Portal.Helpers
public class SbmSessionContextHelper_Portal
{
//-------------------------------------------------------------------
// Control Session Contexts.
// Control Session Context.
//-------------------------------------------------------------------
public static SbmSessionContext GetControlSessionContext(String dbIdentifier)
public static SbmSessionContext GetControlSessionContextByRepositoryContext(String repositoryContext)
{
SbmApplication_Portal app = SbmApplication_Portal.Instance;
return app?.GetControlSessionContext(dbIdentifier);
return app?.GetControlSessionContextByRepositoryContext(repositoryContext);
}
//-------------------------------------------------------------------
// Session Contexts.
//-------------------------------------------------------------------
public static List<SbmSessionContext> GetSessionContextsByRepositoryContext(String repositoryContext)
{
Dictionary<int, SbmSessionContext> sessionContexts = new Dictionary<int, SbmSessionContext>();
SbmApplication_Portal app = SbmApplication_Portal.Instance;
// Control session context.
// - Will be populated if not previously referenced by Repository Context.
SbmSessionContext controlSessionContext = app.GetControlSessionContextByRepositoryContext(repositoryContext);
if (controlSessionContext != null)
sessionContexts[controlSessionContext.Index] = controlSessionContext;
// Core Session Contexts.
foreach (ushort sessionContextIdx in app.GetSessionContextIdxs())
{
// Retrieve the session context.
SbmSessionContext sessionContext = app.GetSessionContextByIndex(sessionContextIdx);
if (sessionContext == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve SessionContext for idx: " + sessionContextIdx);
continue;
}
// Verify Repository Context.
HashSet<string> repositoryContexts = app.GetRepositoryContexts(sessionContext);
if (!repositoryContexts.ContainsCI(repositoryContext))
continue;
sessionContexts[sessionContext.Index] = sessionContext;
}
return new List<SbmSessionContext>(sessionContexts.Values);
}
//-------------------------------------------------------------------
// Session Contexts for Repository Context and User Email Address.
//-------------------------------------------------------------------
/// <summary>
/// Identify the session context(s) that corresponds with the BusinessMgr
/// repository by context for the specified user by email address.
/// </summary>
public static List<SbmSessionContext> GetSessionContextForRepositoryContextUserEmail(String repositoryContext, String emailAddress)
{
Dictionary<int, SbmSessionContext> sessionContexts = new Dictionary<int, SbmSessionContext>();
String alias_SecurityItem = "SecItm";
String alias_Affiliate = "Affil";
if (StringUtil.IsEmpty(emailAddress)) return new List<SbmSessionContext>(sessionContexts.Values);
if (SacsSystem.DebugMode) SaLogger.Info($"{SysUtil.Fn} Searching for database. Identifier: {repositoryContext}. Email address: {emailAddress}");
// Get the session context indices defined by the application.
SbmApplication_Web app = SbmGlobals_Web.Application;
foreach (SbmSessionContext sessionContext in GetSessionContextsByRepositoryContext(repositoryContext))
{
// Verify Repository Context.
HashSet<string> repositoryContexts = app.GetRepositoryContexts(sessionContext);
if (!repositoryContexts.ContainsCI(repositoryContext))
continue;
// Retrieve database.
ISacsDatabase db = app.GetBusMgrDatabase(sessionContext);
if (db == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Database for idx: " + sessionContext.Index);
continue;
}
if (SacsSystem.DebugMode) SaLogger.Info($"{SysUtil.Fn} Database: " + db.DatabaseName);
// Query to check for Security Item matching the email with associated Affiliate.
// NB This is done independently for each database in case there are different
// DBMSs involved with different value formatting or syntax rules.
DbQuery qry = new DbQuery(db.DbmsType);
qry.Tables.Add(SaSecurityDbDfn.GetTableName(SaSecurityDbDfn.TableIdx.SaSecItem), alias_SecurityItem);
qry.Columns.Add(SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_Id, alias_SecurityItem));
qry.Columns.Add(SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_Id, alias_SecurityItem));
qry.Filters.Add(SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress, alias_SecurityItem) + "='" + DbQuery.FormatForCondition(emailAddress) + "'");
// Join to the Affiliate table.
DbQueryJoin joinAffiliate = new DbQueryJoin(DbJoinType.FullOuter);
joinAffiliate.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), alias_Affiliate);
joinAffiliate.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_UserId, alias_Affiliate) + "=" + SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_Id, alias_SecurityItem));
// Execute query.
using (DbResultSet resultSet = DbResultSet_DataReader.ExecuteQuery(db, qry.BuildQuery()))
{
if (resultSet != null && resultSet.NextRecord())
{
if (SacsSystem.DebugMode) SaLogger.Info($"{SysUtil.Fn} Database contains email: " + db.DatabaseName);
sessionContexts[sessionContext.Index] = sessionContext;
}
}
}
return new List<SbmSessionContext>(sessionContexts.Values); ;
}
//-------------------------------------------------------------------
@@ -2,6 +2,7 @@
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Accounts.Sage.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Buffers;
@@ -81,13 +82,14 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
Status,
CreatedOn,
UpdatedOn,
Actions,
}
/// <summary>
/// Identifier used by form fields to show control specific messages generated by model validation etc.
/// <para>- Both page and model should use this method to consistently generate the field name.</para>
/// </summary>
public String GetFieldName(ItemField field)
public static String GetFieldName(ItemField field)
{
return $"{FieldNamePrefix}{field}";
}
@@ -95,6 +97,19 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
// List configuration.
public class ListConfigData
{
// Actions.
public class ActionData
{
public class AllowData
{
public bool? CreateCust { get; set; }
public bool? UpdateCust { get; set; }
public bool? CreateSupp { get; set; }
public bool? UpdateSupp { get; set; }
}
public AllowData Allow { get; } = new AllowData();
}
public ActionData Action { get; } = new ActionData();
// List columns.
public class ColumnData
{
@@ -120,15 +135,29 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
}
public void PopulateListConfig(ListConfigData config)
{
bool modeIsRelationships = Mode == AssociationPageMode.Relationships;
bool modeIsMaintain = Mode == AssociationPageMode.Maintain;
Sbm.AssociationType associationType = GetAssociationType();
AssociationViewMode pageMode = GetFilterMode();
bool modeIsRelationships = pageMode == AssociationViewMode.Relationships;
bool modeIsMaintain = pageMode == AssociationViewMode.Maintain;
Sbm.AssociationType associationType = GetFilterAssociationType();
bool typeIsCustomer = associationType == Sbm.AssociationType.Customer;
bool typeIsSupplier = associationType == Sbm.AssociationType.Supplier;
bool typeIsMember = associationType == Sbm.AssociationType.Member;
bool userCanViewFinancials = SessionData_Portal.SecurityProfile_Core.UserCanViewFinancials();
// Actions.
if (typeIsCustomer)
{
config.Action.Allow.CreateCust = true;
config.Action.Allow.UpdateCust = true;
}
if (typeIsSupplier)
{
config.Action.Allow.CreateSupp = true;
config.Action.Allow.UpdateSupp = true;
}
// List columns.
if (associationType > 0)
@@ -192,6 +221,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
if (!typeIsMember)
config.Column.Hide.Add($"{ItemField.Terminated}");
// Actions.
if (!modeIsMaintain)
config.Column.Hide.Add($"{ItemField.Actions}");
}
//-------------------------------------------------------------------
@@ -220,7 +252,19 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
public class ItemConfigData
{
// Update dialog title.
public String Title{ get; set; }
public String Title { get; set; }
// Pre-select Association type.
public class TypeData
{
public TypeData(string code, String desc)
{
Code = code;
Desc = desc;
}
public String Code { get; set; }
public String Desc { get; set; }
}
public TypeData Type { get; set; }
// Field name prefix.
public String Prefix { get; } = Model_Association.FieldNamePrefix;
// Control flags.
@@ -282,6 +326,15 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
isSupplier ? Localiser["caption.supplier.update"] :
Localiser["caption.association.update"];
// Pre-selected Association type.
List<TypeCodeDesc> associationTypes = Internals.AssociationTypeCodes;
if (isCustomer)
config.Type = new ItemConfigData.TypeData(associationTypes.GetCodeForType(Sbm.AssociationType.Customer), Localiser.GetAssociationTypeCaption(Sbm.AssociationType.Customer));
else if (isSupplier)
config.Type = new ItemConfigData.TypeData(associationTypes.GetCodeForType(Sbm.AssociationType.Supplier), Localiser.GetAssociationTypeCaption(Sbm.AssociationType.Supplier));
else if (isMember)
config.Type = new ItemConfigData.TypeData(associationTypes.GetCodeForType(Sbm.AssociationType.Member), Localiser.GetAssociationTypeCaption(Sbm.AssociationType.Member));
// Allow actions.
if (SessionData_Portal.GetOwnerOption_AllowOption(Sbm.OrgOption_AllowOption.UpdateAssociationCode))
config.Allow.UpdateCode = true;
@@ -345,7 +398,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
// Credit limit.
if (!canViewFinancials || !managedByAccounts)
config.Field.Hide.Add($"{ItemField.CreditLimit}");
else if (managedByAccounts||!canUpdateFinancials)
else if (managedByAccounts || !canUpdateFinancials)
config.Field.Readonly.Add($"{ItemField.CreditLimit}");
// Account balance.
@@ -1,4 +1,5 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Microsoft.AspNetCore.Mvc.Rendering;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
@@ -24,7 +25,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
{
//-------------------------------------------------------------------
// Definitions.
public enum AssociationPageMode
public enum AssociationViewMode
{
Search, Maintain, Relationships, PriceSet,
}
@@ -38,24 +39,29 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
//-------------------------------------------------------------------
// Properties.
public AssociationPageMode Mode { get; set; } = AssociationPageMode.Maintain;
//-------------------------------------------------------------------
// Filter properties.
// Filter.
public class FilterData
{
public String Mode { get; set; }
public string OwnerId { get; set; }
public string TargetId { get; set; }
public string AssociationType { get; set; }
public string Search { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public AssociationViewMode GetFilterMode()
{
return EnumUtil.ParseEnum(Filter.Mode, AssociationViewMode.Maintain);
}
public Sbm.AssociationType GetFilterAssociationType()
{
return (Sbm.AssociationType)Internals.AssociationTypeCodes.GetTypeForCode(Filter.AssociationType);
}
//-------------------------------------------------------------------
// Association properties.
// Form.
public class AssociationData
{
public string Exists { get; set; }
@@ -69,13 +69,14 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
Status,
CreatedOn,
UpdatedOn,
Actions,
}
/// <summary>
/// Identifier used by form fields to show control specific messages generated by model validation etc.
/// <para>- Both page and model should use this method to consistently generate the field name.</para>
/// </summary>
public String GetFieldName(ItemField field)
public static String GetFieldName(ItemField field)
{
return $"{FieldNamePrefix}{field}";
}
@@ -113,6 +114,11 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
/// </summary>
public void PopulateListConfig(ListConfigData config)
{
ContactViewMode viewMode = GetFilterMode();
bool modeIsMaintain= viewMode == ContactViewMode.Maintain;
bool modeIsSearch = viewMode == ContactViewMode.Search;
Sbm.AffiliateType affiliateType = GetFilterContactType();
bool contactGroupsOnly = affiliateType == Sbm.AffiliateType.ContactGroup;
@@ -176,6 +182,18 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
config.Column.Label.Add($"{ItemField.Id}", Localiser["label.id"]);
else
config.Column.Hide.Add($"{ItemField.Id}");
// Associations.
if (modeIsSearch)
config.Column.Hide.Add($"{ItemField.Associations}");
// Status.
if (modeIsSearch)
config.Column.Hide.Add($"{ItemField.Status}");
// Actions.
if (!modeIsMaintain)
config.Column.Hide.Add($"{ItemField.Actions}");
}
//-------------------------------------------------------------------
@@ -23,6 +23,13 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
/// </summary>
public partial class Model_Contact : SbmModel_Portal
{
//-------------------------------------------------------------------
// Definitions.
public enum ContactViewMode
{
Search, Maintain,
}
//-------------------------------------------------------------------
// Construction.
public Model_Contact(SbmLocaliser localiser) :
@@ -34,11 +41,16 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
// Filter properties.
public class FilterData
{
public String Mode { get; set; }
public string ContactType { get; set; }
public string Search { get; set; }
public string Email { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public ContactViewMode GetFilterMode()
{
return EnumUtil.ParseEnum(Filter.Mode, ContactViewMode.Maintain);
}
public Sbm.AffiliateType GetFilterContactType()
{
Sbm.AffiliateType affiliateType = (Sbm.AffiliateType)Internals.AffiliateTypeCodes.GetTypeForCode(Filter.ContactType);
@@ -47,15 +59,6 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
return affiliateType;
}
/// <summary>List of Affiliate Types for filter dropdown.</summary>
public IEnumerable<SelectListItem> GetAffiliateTypeCodesForFilterSelect()
{
List<TypeCodeDesc> affiliateTypes = GetCodesOfType(Sbm.SystemCode.AffiliateType);
if (!affiliateTypes.Any(item => item.Type == (int)Sbm.AffiliateType.AnyOrganisation))
affiliateTypes.Insert(0, new TypeCodeDesc((int)Sbm.AffiliateType.AnyOrganisation, ((short)Sbm.AffiliateType.AnyOrganisation).ToString(), Localiser["label.organisation"]));
return new SelectList(affiliateTypes, "Code", "Desc");
}
//-------------------------------------------------------------------
// Contact properties.
public class ContactData
@@ -244,6 +247,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
if (Contact.CompanyNo != null)
{
String controlName = GetFieldName(ItemField.CompanyNo);
Contact.CompanyNo = Contact.CompanyNo.ToUpper();
responseData.SetControlValue(controlName, Contact.CompanyNo);
if (StringUtil.GetLength(Contact.CompanyNo) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_CompanyNo))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_CompanyNo)]);
@@ -253,6 +257,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Models.Contacts
if (Contact.VatReg != null)
{
String controlName = GetFieldName(ItemField.VatReg);
Contact.VatReg = Contact.VatReg.ToUpper();
responseData.SetControlValue(controlName, Contact.VatReg);
if (StringUtil.GetLength(Contact.VatReg) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_VatReg))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_VatReg)]);
@@ -31,7 +31,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Models
//-------------------------------------------------------------------
// Form properties.
/// <summary>Database config identifier.</summary>
public String DbIdentifier { get; set; }
public String RepositoryContext { get; set; }
public String EmailAddress { get; set; }
public String PasswordCurrent { get; set; }
public String Password { get; set; }
@@ -48,9 +48,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Models
{
{
// Database context.
String controlName = nameof(DbIdentifier);
responseData.SetControlValue(controlName, DbIdentifier);
if (StringUtil.IsEmpty(DbIdentifier))
String controlName = nameof(RepositoryContext);
responseData.SetControlValue(controlName, RepositoryContext);
if (StringUtil.IsEmpty(RepositoryContext))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
}
@@ -114,9 +114,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Models
{
{
// Database context.
String controlName = nameof(DbIdentifier);
responseData.SetControlValue(controlName, DbIdentifier);
if (StringUtil.IsEmpty(DbIdentifier))
String controlName = nameof(RepositoryContext);
responseData.SetControlValue(controlName, RepositoryContext);
if (StringUtil.IsEmpty(RepositoryContext))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
}
@@ -1,8 +1,11 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Microsoft.AspNetCore.Mvc.Rendering;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Web.Portal.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -26,6 +29,37 @@ namespace Sarand.BusinessMgr.Web.Portal.Models
public override ISbmUserContext_Core UserContext_Core { get { return SessionData_Portal; } }
public String OrganisationName { get { return SessionData_Portal?.Organisation?.DisplayName ?? ""; } }
//-------------------------------------------------------------------
// View reference data.
/// <summary>
/// List of Affiliate Types for filter dropdown.
/// <para>- Contact model cannot define this as contact list/dialog may be included in other views with their own model, eg. Association page.</para>
/// </summary>
public IEnumerable<SelectListItem> GetAffiliateTypeCodesForFilterSelect()
{
List<TypeCodeDesc> affiliateTypes = GetCodesOfType(Sbm.SystemCode.AffiliateType);
if (!affiliateTypes.Any(item => item.Type == (int)Sbm.AffiliateType.AnyOrganisation))
affiliateTypes.Insert(0, new TypeCodeDesc((int)Sbm.AffiliateType.AnyOrganisation, ((short)Sbm.AffiliateType.AnyOrganisation).ToString(), Localiser["label.organisation"]));
return new SelectList(affiliateTypes, "Code", "Desc");
}
/// <summary>
/// List of Association Types for filter dropdown.
/// <para>- Association model cannot define this as association list/dialog may be included in other views with their own model, eg. Commodity page.</para>
/// </summary>
public IEnumerable<SelectListItem> GetAssociationTypeCodesForFilterSelect()
{
List<TypeCodeDesc> associationTypes = GetCodesOfType(Sbm.SystemCode.AssociationType);
associationTypes.RemoveAll(item => item.Type == (short)Sbm.AssociationType.Internal);
foreach (Sbm.AssociationType associationType in EnumUtil.GetValues<Sbm.AssociationType>())
{
if (!SessionConsole.AllowAssociationType(associationType))
associationTypes.RemoveAll(item => item.Type == (short)associationType);
}
return new SelectList(associationTypes, "Code", "Desc");
}
//-------------------------------------------------------------------
}
//=======================================================================
@@ -40,6 +40,10 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="Views\Association\Panel-AssociationList.cshtml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
@@ -121,13 +121,13 @@
</div>
</div>
<div class="col-sm-9">
<div class="sbc-control row">
<div class="sbm-control row">
<div class="col">
@Html.HiddenFor(model => model.Asset.CategoryId, new { @id = "", @class = "AssetCategoryId" })
<input type="text" class="AssetCategory sbm-input" readonly="readonly" />
</div>
<div class="fixed">
<div class="sbc-action AssetCategorySearch"><img src="~/img/action/search24.png" alt="" title="" /></div>
<div class="sbm-action AssetCategorySearch"><img src="~/img/action/search24.png" alt="" title="" /></div>
</div>
</div>
<div class="sbm-message"></div>
@@ -1,120 +1,8 @@
@model Model_Association
@inject SbmLocaliser Localiser
@{
/* Associations - Index */
/* Association page */
ViewData["Title"] = "Associations";
Model.AdditionalFiles_JS.Add("js/sbm-assoc.js");
}
<div class="sbm-panel Associations fullheight">
<script type="text/javascript">
sbm.lbl.createCust= '@(Localiser.GetJavascriptText("caption.customer.create"))';
sbm.lbl.createSupp= '@(Localiser.GetJavascriptText("caption.supplier.create"))';
</script>
<div class="sbm-panel-data sbm-flex">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociationFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
@* Header - Page caption, paging controls, actions. *@
<div class="sbm-panel-header">
<div class="row">
<div class="col">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="fixed center"><div class="sbm-panel-caption">@Localiser["label.associations"]</div></div>
<div class="col right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
@* Association Category *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-7 col-sm-8">
@Html.DropDownListFor(model => model.Filter.AssociationType, Model.GetAssociationTypeCodesForSelect(), Localiser["label.any"], new { @id = "", @class = "FilterAssociationType sbm-select" })
</div>
</div>
<div class="col-sm-4 row pb4">
@* Association filter - name etc. *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-7 col-sm-8">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch sbm-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
}
@* Item list *@
<div class="sbm-panel-item-table sbm-flex">
<table class="sbm-item-list">
<thead>
<tr>
<th class="@Model.GetFieldName(Model_Association.ItemField.Type) width10pc pl-1">@Localiser["label.type"]</th>
<th class="@Model.GetFieldName(Model_Association.ItemField.Code) width10pc pl-1">@Localiser["label.code"]</th>
<th class="@Model.GetFieldName(Model_Association.ItemField.Target) width25pc">@Localiser["label.name"]</th>
<th class="@Model.GetFieldName(Model_Association.ItemField.Address) width25pc d-none d-md-table-cell">@Localiser["label.address"]</th>
<th class="@Model.GetFieldName(Model_Association.ItemField.Notes) width25pc d-none d-md-table-cell">@Localiser["label.notes"]</th>
<th class="@Model.GetFieldName(Model_Association.ItemField.Status) width10pc center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu AssociationList">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* Association row template - table is ignored. *@
<table>
<tr class="sbm-row-item Association">
<td class="@Model.GetFieldName(Model_Association.ItemField.Type) pl-1"></td>
<td class="@Model.GetFieldName(Model_Association.ItemField.Code) pl-1"></td>
<td class="@Model.GetFieldName(Model_Association.ItemField.Target)"></td>
<td class="@Model.GetFieldName(Model_Association.ItemField.Address) d-none d-md-table-cell"></td>
<td class="@Model.GetFieldName(Model_Association.ItemField.Notes) d-none d-md-table-cell"></td>
<td class="@Model.GetFieldName(Model_Association.ItemField.Status) center"></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu Association">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
</div>
</div>
<partial name="Panel-AssociationDialog.cshtml" />
<partial name="Panel-AssociationMaintain.cshtml" />
@@ -1,7 +1,10 @@
@model Model_Association
@* View is decoupled from Association model as it may be included in other views, eg. Contacts, Commodities etc. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Associations - Association dialog panel */
/* Association dialog panel */
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Association/Panel-AssociationDialog")) return;
}
<script type="text/javascript">
@@ -11,35 +14,63 @@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociationId", @class = "sbm-form" }))
{
@Html.HiddenFor(model => model.Association.Id, new { @class = "AssociationId" })
<input type="hidden" name="Association.Id" class="AssociationId" />
}
<div id="dlgAssociation" class="sbm-dialog" title="">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociation", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.Association.Id, new { @id = "", @class = "AssociationId sbm-input" })
@Html.HiddenFor(model => model.Association.Exists, new { @id = "", @class = "AssociationExists sbm-input" })
<input type="hidden" name="Association.Id" id="" class="AssociationId sbm-input" />
<input type="hidden" name="Association.Exists" id="" class="AssociationExists sbm-input" />
@* Association Type *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.Type)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbAssociationType"><span>@Localiser["label.type"]</span><span class="required">&nbsp;*</span></label>
<div class="row">
<div class="col-sm-6">
@* Association Type *@
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Type)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
<label for="cbAssociationType"><span>@Localiser["label.type"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
<select name="Association.Type" id="cbAssociationType" class="AssociationType sbm-select"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.DropDownListFor(model => model.Association.Type, Model.GetAssociationTypeCodesForSelect(), Localiser["label.select"], new { @id = "cbAssociationType", @class = "AssociationType sbm-select sbm-auto-val" })
</div>
<div class="col-sm-1"></div>
<div class="col-sm">
@* Association status *@
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Status)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
<label for="cbAssociationStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
<select id="cbAssociationStatus" class="AssociationStatus sbm-select sbm-auto-val" name="Association.StatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Owner contact *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.Owner)">
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Owner)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -47,13 +78,13 @@
</div>
</div>
<div class="col-sm">
<div class="sbc-control row">
<div class="sbm-control row">
<div class="col">
@Html.HiddenFor(model => model.Association.ContactIdOwner, new { @id = "", @class = "AssociationOwnerId sbm-input" })
<input type="hidden" name="Association.ContactIdOwner" id="" class=" AssociationOwnerId sbm-input" />
<input type="text" id="ifAssociationOwner" class="AssociationOwner sbm-input sbm-auto-val" readonly="readonly" />
</div>
<div class="fixed">
@* <div class="sbc-action AssetOwnerSearch"><img src="~/img/action/search24.png" alt="" title="" /></div> *@
@* <div class="sbm-action AssociationOwnerSearch"><img src="~/img/action/search24.png" alt="" title="" /></div> *@
</div>
</div>
<div class="sbm-message"></div>
@@ -62,7 +93,7 @@
</div>
@* Target contact *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.Target)">
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Target)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -70,13 +101,16 @@
</div>
</div>
<div class="col-sm">
<div class="sbc-control row">
<div class="sbm-control row">
<div class="col">
@Html.HiddenFor(model => model.Association.ContactIdTarget, new { @id = "", @class = "AssociationTargetId sbm-input" })
<input type="hidden" name="Association.ContactIdTarget" id="" class=" AssociationTargetId sbm-input" />
<input type="text" id="ifAssociationTarget" class="AssociationTarget sbm-input sbm-auto-val" readonly="readonly" />
</div>
<div class="fixed">
<div class="sbc-action AssetTargetSearch"><img src="~/img/action/search24.png" alt="" title="" /></div>
<div class="sbm-action AssocTargetSearch"><img src="~/img/action/search24.png" alt="" title="" /></div>
</div>
<div class="fixed">
<div class="sbm-action AssocTargetEdit"><img src="~/img/action/edit24.png" alt="" title="" /></div>
</div>
</div>
<div class="sbm-message"></div>
@@ -88,7 +122,7 @@
<div class="col-sm-6">
@* Association Code *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.Code)">
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Code)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -97,7 +131,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Association.Code, new { @id = "ifAssociationCode", @class = "AssociationCode sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Code) })
<input type="text" name="Association.Code" id="ifAssociationCode" class="AssociationCode sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Code)" />
</div>
<div class="sbm-message"></div>
</div>
@@ -109,7 +143,7 @@
<div class="col-sm">
@* Association ExtRef *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.ReferenceExt)">
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.ReferenceExt)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -118,7 +152,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Association.ReferenceExt, new { @id = "ifAssociationExtRef", @class = "AssociationExtRef sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_ExtRef) })
<input type="text" name="Association.ReferenceExt" id="ifAssociationExtRef" class="AssociationExtRef sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_ExtRef)" />
</div>
<div class="sbm-message"></div>
</div>
@@ -128,25 +162,52 @@
</div>
</div>
@* Association status *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.Status)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbAssociationStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
<div class="row">
<div class="col-sm-6">
@* Association Location *@
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Location)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
<label for="ifAssociationLocation"><span>@Localiser["label.location"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
<input type="text" name="Association.Location" id="ifAssociationLocation" class="AssociationLocation sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Location)" />
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
<select id="cbAssociationStatus" class="AssociationStatus sbm-select sbm-auto-val" name="Association.StatusCode"></select>
</div>
<div class="col-sm-1"></div>
<div class="col-sm">
@* Association Group *@
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Group)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
<label for="ifAssociationGroup"><span>@Localiser["label.group"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
<input type="text" name="Association.Group" id="ifAssociationGroup" class="AssociationGroup sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Group)" />
</div>
<div class="sbm-message"></div>
</div>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Association Notes *@
<div class="sbm-form-control @Model.GetFieldName(Model_Association.ItemField.Notes)">
<div class="sbm-form-control @Model_Association.GetFieldName(Model_Association.ItemField.Notes)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -155,7 +216,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.Association.Notes, new { @id = "ifAssociationNotes", @class = "AssociationNotes sbm-textarea sbm-auto-val", @rows = "3", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Notes) })
<textarea name="Association.Notes" id="ifAssociationNotes" class="AssociationNotes sbm-textarea sbm-auto-val" rows="3" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Notes)"></textarea>
</div>
<div class="sbm-message"></div>
</div>
@@ -164,4 +225,9 @@
}
<div class="spacer2"></div>
</div>
@* Contact panels. *@
<partial name="~/Views/Contact/Panel-ContactSelect.cshtml" />
<partial name="~/Views/Contact/Panel-ContactDialog.cshtml" />
@@ -0,0 +1,64 @@
@* View is decoupled from Association model as it may be included in other views, eg. Contacts, Commodities etc. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Association list panel */
/* NB Can potentially appear more than once on a page? */
}
@* Item list *@
<div class="sbm-panel-item-table sbm-flex">
<table class="sbm-item-list">
<thead>
<tr>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Type) width10pc pl-1">@Localiser["label.type"]</th>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Code) width10pc pl-1">@Localiser["label.code"]</th>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Target) width25pc">@Localiser["label.name"]</th>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Address) width25pc d-none d-md-table-cell">@Localiser["label.address"]</th>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Notes) width25pc d-none d-md-table-cell">@Localiser["label.notes"]</th>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Status) width10pc center">@Localiser["label.status"]</th>
<th class="@Model_Association.GetFieldName(Model_Association.ItemField.Actions)" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu AssociationList">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* Association row template - table is ignored. *@
<table>
<tr class="sbm-row-item Association">
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Type) pl-1"></td>
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Code) pl-1"></td>
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Target)"></td>
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Address) d-none d-md-table-cell"></td>
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Notes) d-none d-md-table-cell"></td>
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Status) center"></td>
<td class="@Model_Association.GetFieldName(Model_Association.ItemField.Actions)">
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu Association">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@@ -0,0 +1,68 @@
@* View is decoupled from Association model as it may be included in other views, eg. Contacts, Commodities etc. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Association maintain panel */
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Association/Panel-AssociationMaintain")) return;
Model.AdditionalFiles_JS.Add("js/sbm-assoc.js");
}
<div class="sbm-panel Associations fullheight">
<script type="text/javascript">
sbm.lbl.createCust= '@(Localiser.GetJavascriptText("caption.customer.create"))';
sbm.lbl.createSupp= '@(Localiser.GetJavascriptText("caption.supplier.create"))';
</script>
<div class="sbm-panel-data sbm-flex">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "", @class = "frmAssociationFilter sbm-form", @onSubmit = "return false;" }))
{
@* Header - Page caption, paging controls, actions. *@
<div class="sbm-panel-header">
<div class="row">
<div class="col">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="fixed center"><div class="sbm-panel-caption">@Localiser["label.associations"]</div></div>
<div class="col right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
@* Association Category *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-7 col-sm-8">
@Html.DropDownList("Filter.AssociationType", Model.GetAssociationTypeCodesForFilterSelect(), @Localiser["label.any"], new { @id = "", @class = "FilterAssociationType sbm-select" })
</div>
</div>
<div class="col-sm-4 row pb4">
@* Association filter - name etc. *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-7 col-sm-8">
@Html.TextBox("Filter.Search", "", new { @id = "", @class = "FilterSearch sbm-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
}
<partial name="Panel-AssociationList.cshtml" />
</div>
</div>
<partial name="Panel-AssociationDialog.cshtml" />
@@ -0,0 +1,46 @@
@* View is decoupled from Association model as it may be included in other views, eg. Associations, Commodities etc. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Association select panel */
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Association/Panel-AssociationSelect")) return;
Model.AdditionalFiles_JS.Add("js/sbm-assoc.js");
}
<script type="text/javascript">
</script>
<div id="dlgAssociationSelect" class="sbm-dialog sbm-flex sbm-no-padding" title="@Localiser["caption.association.select"]">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "", @class = "frmAssociationFilter sbm-form", @onSubmit = "return false;" }))
{
@Html.Hidden("Filter.Mode", "Search", new { @id = "" })
@Html.Hidden("Filter.OwnerId", "", new { @id = "", @class = "FilterOwnerId" })
@Html.Hidden("Filter.TargetId", "", new { @id = "", @class = "FilterTargetId" })
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
@* Association type *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-8 col-sm-8">
@Html.DropDownList("Filter.AssociationType", Model.GetAssociationTypeCodesForFilterSelect(), @Localiser["label.any"], new { @id = "", @class = "FilterAssociationType sbm-select" })
</div>
</div>
<div class="col-sm-4 row pb4">
@* Association filter - name etc. *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-8 col-sm-8">
@Html.TextBox("Filter.Search", "", new { @id = "", @class = "FilterSearch sbm-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
}
<partial name="Panel-AssociationList.cshtml" />
</div>
@@ -130,7 +130,7 @@
</div>
</div>
<div class="col-sm-9">
<div class="sbc-control">
<div class="sbm-control">
<select name="model.Code.CodeTypeId" class="CodeType sbm-select" readonly="readonly"></select>
</div>
<div class="sbm-message"></div>
@@ -10,7 +10,7 @@
</script>
@* Results paging *@
<div class="sbm-results-paging hidden">
<div class="sbm-results-paging" style="display:none;">
@Html.HiddenFor(model => model.Paging.PageNo, new { @id = "", @class = "PagingPageNo" })
@Html.HiddenFor(model => model.Paging.PageLength, new { @id = "", @class = "PagingPageLength" })
@@ -1,123 +1,8 @@
@model Model_Contact
@inject SbmLocaliser Localiser
@{
/* Contacts - Index */
/* Contact page */
ViewData["Title"] = "Contacts";
Model.AdditionalFiles_JS.Add("js/sbm-contact.js");
}
<div class="sbm-panel Contacts fullheight">
<script type="text/javascript">
sbm.lbl.createOrg = '@(Localiser.GetJavascriptText("caption.organisation.create"))';
sbm.lbl.createInd = '@(Localiser.GetJavascriptText("caption.individual.create"))';
sbm.lbl.createGrp = '@(Localiser.GetJavascriptText("caption.contact.group.create"))';
</script>
<div class="sbm-panel-data sbm-flex">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmContactFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
@* Header - Page caption, paging controls, actions. *@
<div class="sbm-panel-header">
<div class="row">
<div class="col"><partial name="~/Views/Common/ResultsPaging.cshtml" /></div>
<div class="fixed center"><div class="sbm-panel-caption">@Localiser["label.contacts"]</div></div>
<div class="col right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
@* Contact type *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-8 col-sm-8">
@Html.DropDownListFor(model => model.Filter.ContactType, Model.GetAffiliateTypeCodesForFilterSelect(), Localiser["label.any"], new { @id = "", @class = "FilterContactType sbm-select" })
</div>
</div>
<div class="col-sm-4 row pb4">
@* Contact filter - name etc. *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-8 col-sm-8">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch sbm-input", @maxlength = "32" })
</div>
</div>
</div>
</div>
}
@* Item list *@
<div class="sbm-panel-item-table sbm-flex">
<table class="sbm-item-list">
<thead>
<tr>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Type) width10pc pl-1">@Localiser["label.type"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Name) width25pc pl-1">@Localiser["label.name"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Address) width25pc d-none d-md-table-cell">@Localiser["label.address"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Contact) width10pc d-none d-md-table-cell">@Localiser["label.contact"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Phone) width10pc d-none d-sm-table-cell">@Localiser["label.phone"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Notes) width25pc d-none d-md-table-cell">@Localiser["label.notes"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Associations) width10pc center">@Localiser["label.associations"]</th>
<th class="@Model.GetFieldName(Model_Contact.ItemField.Status) width10pc center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu ContactList">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* Item row template - table is ignored. *@
<table>
<tr class="sbm-row-item Contact">
<td class="@Model.GetFieldName(Model_Contact.ItemField.Type) pl-1"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Name) pl-1"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Address) d-none d-md-table-cell"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Contact) d-none d-md-table-cell"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Phone) d-none d-sm-table-cell"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Notes) d-none d-md-table-cell"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Associations) center"></td>
<td class="@Model.GetFieldName(Model_Contact.ItemField.Status) center"></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu Contact">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
</div>
</div>
<partial name="Panel-ContactDialog.cshtml" />
<partial name="Panel-ContactMaintain.cshtml" />
@@ -1,7 +1,10 @@
@model Model_Contact
@* View is decoupled from Contact model as it may be included in other views, eg. Associations. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Contacts - Contactdialog panel */
/* Contact dialog panel */
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Contact/Panel-ContactDialog")) return;
}
<script type="text/javascript">
@@ -10,23 +13,23 @@
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmContactId", @class = "sbm-form" }))
{
@Html.HiddenFor(model => model.Contact.Id, new { @class = "ContactId" })
<input type="hidden" name="Contact.Id" class="ContactId" />
}
<div id="dlgContact" class="sbm-dialog" title="">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmContact", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.Contact.Id, new { @id = "", @class = "ContactId sbm-input" })
@Html.HiddenFor(model => model.Contact.Exists, new { @id = "", @class = "ContactExists sbm-input" })
@Html.HiddenFor(model => model.Contact.IsIndividual, new { @id = "", @class = "ContactIsIndividual sbm-input" })
@Html.HiddenFor(model => model.Contact.IsGroup, new { @id = "", @class = "ContactIsGroup sbm-input" })
<input type="hidden" name="Contact.Id" id="" class="ContactId sbm-input" />
<input type="hidden" name="Contact.Exists" id="" class="ContactExists sbm-input" />
<input type="hidden" name="Contact.IsIndividual" id="" class="ContactIsIndividual sbm-input" />
<input type="hidden" name="Contact.IsGroup" id="" class="ContactIsGroup sbm-input" />
<div class="row">
<div class="col-sm-6">
@* Contact Type *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Type)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Type)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -47,7 +50,7 @@
<div class="col-sm">
@* Organisation Abbreviation *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Abbreviation)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Abbreviation)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -56,7 +59,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Abbreviation, new { @id = "ifContactAbbreviation", @class = "ContactAbbreviation sbm-input sbm-auto-val upper", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Abbrev) })
<input type="text" name="Contact.Abbreviation" id="ifContactAbbreviation" class="ContactAbbreviation sbm-input sbm-auto-val upper" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Abbrev)" />
</div>
</div>
</div>
@@ -67,7 +70,7 @@
</div>
@* Organisation Name *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Name)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Name)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -76,7 +79,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Name, new { @id = "ifContactName", @class = "ContactName sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Name) })
<input type="text" name="Contact.Name" id="ifContactName" class="ContactName sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Name)" />
</div>
</div>
</div>
@@ -87,7 +90,7 @@
<div class="col-sm-6">
@* Nature Organisation Nature *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Nature)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Nature)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -96,7 +99,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Nature, new { @id = "ifContactNature", @class = "ContactNature sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Nature) })
<input type="text" name="Contact.Nature" id="ifContactNature" class="ContactNature sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Nature)" />
</div>
</div>
</div>
@@ -108,7 +111,7 @@
<div class="col-sm">
@* Organisation Contact *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Contact)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Contact)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -117,7 +120,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Contact, new { @id = "ifContactContact", @class = "ContactContact sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Contact) })
<input type="text" name="Contact.Contact" id="ifContactContact" class="ContactContact sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Contact)" />
</div>
</div>
</div>
@@ -131,7 +134,7 @@
<div class="col-sm-6">
@* Individual Title *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Title)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Title)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -140,7 +143,12 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.DropDownListFor(model => model.Contact.Title, Model.GetCodeTypeCodesForSelect(Sbm.ApplCodeType.ContactTitle), new { @id = "cbContactTitle", @class = "ContactTitle sbm-select sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Title) })
<select name="Contact.Title" id="cbContactTitle" class="ContactTitle sbm-select sbm-auto-val">
@foreach (var title in Model.GetCodeTypeCodesForSelect(Sbm.ApplCodeType.ContactTitle))
{
<option value="@title.Value">@title.Text</option>
}
</select>
</div>
</div>
</div>
@@ -152,7 +160,7 @@
<div class="col-sm">
@* Individual Salutation *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Salutation)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Salutation)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -161,7 +169,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Salutation, new { @id = "ifContactSalutation", @class = "ContactSalutation sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Salutation) })
<input type="text" name="Contact.Salutation" id="ifContactSalutation" class="ContactSalutation sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Salutation)" />
</div>
</div>
</div>
@@ -175,7 +183,7 @@
<div class="col-sm-6">
@* Individual First Name *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.FirstName)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.FirstName)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -184,7 +192,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.FirstName, new { @id = "ifContactFirstName", @class = "ContactFirstName sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_FirstName) })
<input type="text" name="Contact.FirstName" id="ifContactFirstName" class="ContactFirstName sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_FirstName)" />
</div>
</div>
</div>
@@ -196,7 +204,7 @@
<div class="col-sm">
@* Individual Initials *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Initials)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Initials)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -205,7 +213,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Initials, new { @id = "ifContactInitials", @class = "ContactInitials sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Initials) })
<input type="text" name="Contact.Initials" id="ifContactInitials" class="ContactInitials sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Initials)" />
</div>
</div>
</div>
@@ -216,7 +224,7 @@
</div>
@* Individual Last Name *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.LastName)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.LastName)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -225,7 +233,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.LastName, new { @id = "ifContactLastName", @class = "ContactLastName sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_LastName) })
<input type="text" name="Contact.LastName" id="ifContactLastName" class="ContactLastName sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_LastName)" />
</div>
</div>
</div>
@@ -236,7 +244,7 @@
<div class="col-sm-6">
@* Organisation Main Phone & Individual Home Phone *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.PhoneMain)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.PhoneMain)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -245,7 +253,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.PhoneMain, new { @id = "ifContactPhoneMain", @class = "ContactPhoneMain sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneMain) })
<input type="text" name="Contact.PhoneMain" id="ifContactPhoneMain" class="ContactPhoneMain sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneMain)" />
</div>
</div>
</div>
@@ -257,7 +265,7 @@
<div class="col-sm">
@* Organisation Alt Phone *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.PhoneAlt)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.PhoneAlt)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -266,7 +274,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.PhoneAlt, new { @id = "ifContactPhoneAlt", @class = "ContactPhoneAlt sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneAlt) })
<input type="text" name="Contact.PhoneAlt" id="ifContactPhoneAlt" class="ContactPhoneAlt sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneAlt)" />
</div>
</div>
</div>
@@ -274,7 +282,7 @@
</div>
@* Individual Mobile Phone *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.PhoneMobile)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.PhoneMobile)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -283,7 +291,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.PhoneMobile, new { @id = "ifContactPhoneMobile", @class = "ContactPhoneMobile sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneMobile) })
<input type="text" name="Contact.PhoneMobile" id="ifContactPhoneMobile" class="ContactPhoneMobile sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneMobile)" />
</div>
</div>
</div>
@@ -297,7 +305,7 @@
<div class="col-sm-6">
@* Organisation Direct Phone *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.PhoneDirect)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.PhoneDirect)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -306,7 +314,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.PhoneDirect, new { @id = "ifContactPhoneDirect", @class = "ContactPhoneDirect sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneDirect) })
<input type="text" name="Contact.PhoneDirect" id="ifContactPhoneDirect" class="ContactPhoneDirect sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneDirect)" />
</div>
</div>
</div>
@@ -318,7 +326,7 @@
<div class="col-sm">
@* Organisation Fax *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.PhoneFax)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.PhoneFax)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -327,7 +335,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.PhoneFax, new { @id = "ifContactPhoneFax", @class = "ContactPhoneFax sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneFax) })
<input type="text" name="Contact.PhoneFax" id="ifContactPhoneFax" class="ContactPhoneFax sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_PhoneFax)" />
</div>
</div>
</div>
@@ -338,7 +346,7 @@
</div>
@* Contact Email *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Email)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Email)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -347,7 +355,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Email, new { @id = "ifContactEmail", @class = "ContactEmail sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Email) })
<input type="text" name="Contact.Email" id="ifContactEmail" class="ContactEmail sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Email)" />
</div>
</div>
</div>
@@ -355,7 +363,7 @@
</div>
@* Contact Website *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Website)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Website)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -364,7 +372,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.Website, new { @id = "ifContactWebsite", @class = "ContactWebsite sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Website) })
<input type="text" name="Contact.Website" id="ifContactWebsite" class="ContactWebsite sbm-input sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Website)" />
</div>
</div>
</div>
@@ -375,7 +383,7 @@
<div class="col-sm-6">
@* Organisation Company Number *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.CompanyNo)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.CompanyNo)">
<div class="row">
<div class="col-sm-6">
<div class="sbm-label">
@@ -384,7 +392,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.CompanyNo, new { @id = "ifContactCompanyNo", @class = "ContactCompanyNo sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_CompanyNo) })
<input type="text" name="Contact.CompanyNo" id="ifContactCompanyNo" class="ContactCompanyNo sbm-input upper sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_CompanyNo)" />
</div>
</div>
</div>
@@ -396,7 +404,7 @@
<div class="col-sm">
@* Organisation VAT registration number *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.VatReg)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.VatReg)">
<div class="row">
<div class="col-sm-5">
<div class="sbm-label">
@@ -405,7 +413,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Contact.VatReg, new { @id = "ifContactVatReg", @class = "ContactVatReg sbm-input sbm-auto-val", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_VatReg) })
<input type="text" name="Contact.VatReg" id="ifContactVatReg" class="ContactVatReg sbm-input upper sbm-auto-val" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_VatReg)" />
</div>
</div>
</div>
@@ -416,7 +424,7 @@
</div>
@* Contact Options *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Options)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Options)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -424,11 +432,11 @@
</div>
</div>
<div class="col-sm">
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Prospect)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Prospect)">
<div class="sbm-control">
<input type="checkbox" name="Contact.Prospect" id="chkContactProspect" class="ContactProspect sbm-checkbox mt-1" value="true" />
<input type="checkbox" name="Contact.Prospect" class="sbm-checkbox hidden" value="false" />
&nbsp;<span class="sbc-label pointer" onclick="$('#chkContactProspect').click();">@Localiser["label.prospect"]</span>
&nbsp;<span class="sbm-label pointer" onclick="$('#chkContactProspect').click();">@Localiser["label.prospect"]</span>
</div>
<div class="sbm-message"></div>
</div>
@@ -437,7 +445,7 @@
</div>
@* Contact Notes *@
<div class="sbm-form-control @Model.GetFieldName(Model_Contact.ItemField.Notes)">
<div class="sbm-form-control @Model_Contact.GetFieldName(Model_Contact.ItemField.Notes)">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
@@ -446,7 +454,7 @@
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.Contact.Notes, new { @id = "ifContactNotes", @class = "ContactNotes sbm-textarea sbm-auto-val", @rows = "3", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Notes) })
<textarea name="Contact.Notes" id="ifContactNotes" class="ContactNotes sbm-textarea sbm-auto-val" rows="3" maxlength="@Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Affiliate_Notes)"></textarea>
</div>
</div>
</div>
@@ -455,4 +463,9 @@
}
<div class="spacer2"></div>
</div>
@* Association panels. *@
<partial name="~/Views/Association/Panel-AssociationSelect.cshtml" />
<partial name="~/Views/Association/Panel-AssociationDialog.cshtml" />
@@ -0,0 +1,71 @@
@* View is decoupled from Contact model as it may be included in other views, eg. Associations. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Contact list panel */
/* NB Can potentially appear more than once on a page? */
}
<script type="text/javascript">
</script>
@* Item list *@
<div class="sbm-panel-item-table sbm-flex">
<table class="sbm-item-list">
<thead>
<tr>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Type) width10pc pl-1">@Localiser["label.type"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Name) width25pc pl-1">@Localiser["label.name"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Address) width25pc d-none d-md-table-cell">@Localiser["label.address"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Contact) width10pc d-none d-md-table-cell">@Localiser["label.contact"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Phone) width10pc d-none d-sm-table-cell">@Localiser["label.phone"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Notes) width25pc d-none d-md-table-cell">@Localiser["label.notes"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Associations) width10pc center">@Localiser["label.associations"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Status) width10pc center">@Localiser["label.status"]</th>
<th class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Actions) right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu ContactList">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* Item row template - table is ignored. *@
<table>
<tr class="sbm-row-item Contact">
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Type) pl-1"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Name) pl-1"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Address) d-none d-md-table-cell"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Contact) d-none d-md-table-cell"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Phone) d-none d-sm-table-cell"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Notes) d-none d-md-table-cell"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Associations) center"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Status) center"></td>
<td class="@Model_Contact.GetFieldName(Model_Contact.ItemField.Actions)">
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu Contact">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@@ -0,0 +1,73 @@
@* View is decoupled from Contact model as it may be included in other views, eg. Associations. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Contact maintain panel */
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Contact/Panel-ContactMaintain")) return;
Model.AdditionalFiles_JS.Add("js/sbm-contact.js");
}
<div class="sbm-panel Contacts fullheight">
<script type="text/javascript">
sbm.lbl.createOrg = '@(Localiser.GetJavascriptText("caption.organisation.create"))';
sbm.lbl.createInd = '@(Localiser.GetJavascriptText("caption.individual.create"))';
sbm.lbl.createGrp = '@(Localiser.GetJavascriptText("caption.contact.group.create"))';
</script>
<div class="sbm-panel-data sbm-flex">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "", @class = "frmContactFilter sbm-form", @onSubmit = "return false;" }))
{
@* Header - Page caption, paging controls, actions. *@
<div class="sbm-panel-header">
<div class="row">
<div class="col"><partial name="~/Views/Common/ResultsPaging.cshtml" /></div>
<div class="fixed center"><div class="sbm-panel-caption">@Localiser["label.contacts"]</div></div>
<div class="col right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
@* Contact type *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-7 col-sm-8">
<select name="Filter.ContactType" id="" class="FilterContactType sbm-select">
<option value="">@Localiser["label.any"]</option>
@foreach (var type in Model.GetAffiliateTypeCodesForFilterSelect())
{
<option value="@type.Value">@type.Text</option>
}
</select>
</div>
</div>
<div class="col-sm-4 row pb4">
@* Contact filter - name etc. *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-7 col-sm-8">
<input type="text" name="Filter.Search" id="" class="FilterSearch sbm-input" maxlength="32" />
</div>
</div>
</div>
</div>
}
<partial name="Panel-ContactList.cshtml" />
</div>
</div>
<partial name="Panel-ContactDialog.cshtml" />
@@ -0,0 +1,49 @@
@* View is decoupled from Contact model as it may be included in other views, eg. Associations. *@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
/* Contact select panel */
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Contact/Panel-ContactSelect")) return;
Model.AdditionalFiles_JS.Add("js/sbm-contact.js");
}
<script type="text/javascript">
</script>
<div id="dlgContactSelect" class="sbm-dialog sbm-flex sbm-no-padding" title="@Localiser["caption.contact.select"]">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "", @class = "frmContactFilter sbm-form", @onSubmit = "return false;" }))
{
<input type="hidden" name="Filter.Mode" value="Search" />
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row">
<div class="col-sm-4 row pb4">
@* Contact type *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-8 col-sm-8">
<select name="Filter.ContactType" id="" class="FilterContactType sbm-select">
<option value="">@Localiser["label.any"]</option>
@foreach (var type in Model.GetAffiliateTypeCodesForFilterSelect())
{
<option value="@type.Value">@type.Text</option>
}
</select>
</div>
</div>
<div class="col-sm-4 row pb4">
@* Contact filter - name etc. *@
<div class="col-4 col-sm-4 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-8 col-sm-8">
<input type="text" name="Filter.Search" id="" class="FilterSearch sbm-input" maxlength="32" />
</div>
</div>
</div>
</div>
}
<partial name="Panel-ContactList.cshtml" />
</div>
@@ -15,12 +15,12 @@
{
<div class="spacer4"></div>
<div class="sbm-form-control DbIdentifier">
<div class="sbm-form-control RepositoryContext">
<div class="sbm-label">
<label for="ifEmailAddress"><span>@Localiser["label.context"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.DbIdentifier, new { @id = "ifDbIdentifier", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength })
@Html.TextBoxFor(model => model.RepositoryContext, new { @id = "ifRepositoryContext", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength })
</div>
<div class="sbm-message"></div>
</div>
@@ -17,12 +17,12 @@
{
<div class="spacer2"></div>
<div class="sbm-form-control DbIdentifier">
<div class="sbm-form-control RepositoryContext">
<div class="sbm-label">
<label for="ifEmailAddress"><span>@Localiser["label.context"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.DbIdentifier, new { @id = "ifDbIdentifier", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength })
@Html.TextBoxFor(model => model.RepositoryContext, new { @id = "ifRepositoryContext", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength })
</div>
<div class="sbm-message"></div>
</div>
@@ -125,6 +125,8 @@
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Contact');">@Localiser["label.contacts"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Customers');">@Localiser["label.customers"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Suppliers');">@Localiser["label.suppliers"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Association');">@Localiser["label.associations"]</div>
</div>
</li>
@@ -5,12 +5,19 @@
:root
{
--color-OldGold: #d4af37;
--color-MintGreen: #98fb98;
--color-LimeGreen: #32cd32;
--color-KellyGreen: #4cbb17;
--color-OliveGreen: #808000;
--color-ForestGreen: #228b22;
--color-CornflowerBlue: #6495ed;
--color-RoyalBlue: #4169e1;
--color-NavyBlue: #000080;
--color-DarkBlue: #00008B;
--color-Indigo: #4b0082;
--color-Iris: #5d3fd3;
--color-BrightPurple: #bf40bf;
--color-DarkPink: #aa336a;
}
body
@@ -6,14 +6,24 @@
:root
{
--color-DataPanel-main: var(--color-OliveGreen);
--color-panel-data: var(--color-OliveGreen);
}
.Contacts .sbm-panel-data
{
--color-panel-data: var(--color-ForestGreen);
}
.Associations .sbm-panel-data
{
--color-panel-data: var(--color-Iris);
}
.sbm-panel-data
{
margin: 6px auto;
background-color: #f8f8f8;
border: solid 1px var(--color-DataPanel-main);
border: solid 1px var(--color-panel-data);
border-radius: 6px;
overflow: hidden;
}
@@ -32,8 +42,8 @@
padding: 1px 8px 2px 8px;
min-height: 24px;
color: #f0f0f0;
background-color: var(--color-DataPanel-main);
border-bottom: solid 1px var(--color-DataPanel-main);
background-color: var(--color-panel-data);
border-bottom: solid 1px var(--color-panel-data);
}
.sbm-panel-data .sbm-panel-header .sbm-panel-caption
@@ -65,13 +75,13 @@
/* Data panel - Filter */
.sbm-panel-data .sbm-panel-filter
.sbm-panel-filter
{
padding: 6px 8px;
border-bottom: solid 1px var(--color-DataPanel-main);
padding: 6px 8px 2px 8px;
border-bottom: solid 1px var(--color-panel-data);
}
.sbm-panel-data .sbm-panel-filter .sbm-label
.sbm-panel-filter .sbm-label
{
padding: 2px 8px 0 0;
}
@@ -126,7 +136,8 @@
.sbm-panel-data .sbm-panel-header .sbm-results-paging .sbm-action
{
margin: 2px 1px 0 1px;
margin: 0px 1px;
padding:0;
background-color: #e0e0e0;
opacity: 50%;
box-shadow: 0 0 0 1px #40404080 inset;
@@ -393,7 +404,7 @@ table.sbm-item-list tbody tr:hover
table.sbm-item-list tbody tr.sbm-sel
{
background-color: palegreen;
background-color: #c1f9a2;
}
table.sbm-item-list tbody td
@@ -99,13 +99,21 @@ body > .container, header > .container, footer > .container
{
}
a.navbar-brand
.navbar a.navbar-brand
{
padding-top:0.3rem;
padding-bottom:0.1rem;
white-space: normal;
text-align: center;
word-break: break-all;
}
.navbar li.nav-item a.nav-link
{
padding-top: 0.3rem;
padding-bottom: 0.1rem;
}
.border-top
{
border-top: 1px solid #e5e5e5;
@@ -333,6 +341,16 @@ a:focus
--color-dialog: var(--color-DarkBlue);
}
.ui-dialog.sbm-Contact
{
--color-dialog: var(--color-ForestGreen);
}
.ui-dialog.sbm-Association
{
--color-dialog: var(--color-Iris);
}
.sbm-dialog
{
display: none;
@@ -393,10 +411,21 @@ a:focus
display: none;
}
.sbm-dialog.ui-dialog-content.sbm-flex
{
display: flex !important;
flex-direction: column;
}
.sbm-dialog.ui-dialog-content.sbm-no-padding
{
padding: 0;
}
.ui-dialog .ui-dialog-buttonpane
{
border-color: var(--color-dialog);
margin-top: .25em;
margin-top: 0;
padding: .2em 1em .2em .5em;
}
+254 -117
View File
@@ -5,73 +5,100 @@
(function (win, doc, undef)
{
"use strict";
let as = sbm.Associations = { data: {}, ajax: {} };
let $pnl = as.$pnl = {}, $frm = as.$frm = {}, $dlg = as.$dlg = {};
let as = sbm.mdl.Associations = { env: {} };
//--------------------------------------------------------------
as.refreshResults = function (delay)
as.refreshConfig = function (env, delay)
{
if (!sbm.awaitingRefreshResponse)
{
$pnl.bgr.invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
as.ajax.refreshResults = as.ajax.refreshResults || { url: sbm.url.base + 'Association/Ajax/List', formId: $frm.filter.prop('id'), callbackFn: as.doRefreshResults };
sbm.doAjaxRequest(as.ajax.refreshResults, delay || 50);
sbm.awaitingRefreshResponse = true;
env.ajax.refreshConfig = env.ajax.refreshConfig || { url: sbm.url.base + 'Association/Ajax/Config', formSel: env.$frm.filter, callbackFn: function (response) { as.doRefreshConfig(env, response); } };
sbm.doAjaxRequest(env.ajax.refreshConfig, delay || 50);
}
as.doRefreshResults = function (response)
as.doRefreshConfig = function (env, response)
{
//sbm.debug('doRefreshResults', response);
delete sbm.awaitingRefreshResponse;
$pnl.msgs.hide().empty();
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
sbm.refreshPaging(response, '#frmAssociationFilter', as.refreshResults);
//sbm.debug('doRefreshConfig', env, response);
if (!response.success) return;
// Config.
env.mode = response.mode;
env.config = response.config;
env.configCust = response.configCust;
env.configSupp = response.configSupp;
env.data.associationTypes = response.associationTypes;
env.data.statuses = response.statuses;
env.data.defaultStatusCode = response.defaultStatusCode;
}
// Reference data.
as.config = response.config;
as.configCust = response.configCust;
as.configSupp = response.configSupp;
as.data.associationTypes = response.associationTypes;
as.data.statuses = response.statuses;
as.data.defaultStatusCode = response.defaultStatusCode;
//--------------------------------------------------------------
as.refreshResults = function (env, delay)
{
if (!as.awaitingRefreshResponse)
{
env.$pnl.bgr.invisible();
env.$pnl.table.find('tbody tr').remove();
env.$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
env.ajax.refreshResults = env.ajax.refreshResults || { url: sbm.url.base + 'Association/Ajax/List', formSel: env.$frm.filter, callbackFn: function (response) { as.doRefreshResults(env, response); } };
sbm.doAjaxRequest(env.ajax.refreshResults, delay || 50);
as.awaitingRefreshResponse = true;
}
as.doRefreshResults = function (env, response)
{
//sbm.debug('doRefreshResults', env, response);
delete as.awaitingRefreshResponse;
env.$pnl.msgs.hide().empty();
let $table = $('table', env.$pnl.table).find('tbody tr').remove().end();
sbm.refreshPaging(response, env.$frm.filter, function () { as.refreshResults(env); });
if (!response.success) return;
as.doRefreshConfig(env, response);
if (response.caption) env.$pnl.items.find('.sbm-panel-caption').text(response.caption);
// Menu.
$pnl.bgr.invisible();
$pnl.mnu.empty();
$pnl.mnu.append(as.getFilterMenuOption(sbm.lbl.refresh, function () { as.refreshResults(); }, 'sbm-refresh'));
$pnl.mnu.append(as.getFilterMenuOption(sbm.lbl.createCust, function () { as.createItem(as.configCust, sbm.lbl.createCust); }, 'sbm-add'));
$pnl.mnu.append(as.getFilterMenuOption(sbm.lbl.createSupp, function () { as.createItem(as.configSupp, sbm.lbl.createSupp); }, 'sbm-add'));
$pnl.bgr.toggleVisibility(!!$('.sbm-dropdown-item', $pnl.mnu).length);
if (env.$pnl.bgr.length)
{
env.$pnl.bgr.invisible();
env.$pnl.mnu.empty();
env.$pnl.mnu.append(as.getFilterMenuOption(env, sbm.lbl.refresh, function () { as.refreshResults(env); }, 'sbm-refresh'));
if (env.config.action.allow.createCust)
env.$pnl.mnu.append(as.getFilterMenuOption(env, sbm.lbl.createCust, function () { as.createItem(env, env.configCust, sbm.lbl.createCust); }, 'sbm-add'));
if (env.config.action.allow.createSupp)
env.$pnl.mnu.append(as.getFilterMenuOption(env, sbm.lbl.createSupp, function () { as.createItem(env, env.configSupp, sbm.lbl.createSupp); }, 'sbm-add'));
env.$pnl.bgr.toggleVisibility(!!$('.sbm-dropdown-item', env.$pnl.mnu).length);
}
// Items.
var items = response.associations || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
return env.$pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(as.populateItem(as.getItemTemplate(), items[itemIdx]));
sbm.configureItemTable(as.config, $table);
$table.append(as.populateItem(env, as.getItemTemplate(env), items[itemIdx]));
sbm.configureItemTable(env.config, $table);
}
as.populateItem = function ($item, item)
as.populateItem = function (env, $item, item)
{
if (!item) return;
$item.prop('id', as.getItemId(item));
sbm.populateItemValues(as.config, $item, item.field.value);
let prefix = as.config.column.prefix;
$('.' + prefix + 'Status', $item).removeStatusClasses().addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
var $bgr = $('.sbm-burger', $item).hide();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
let cfg = env.config;
sbm.populateItemValues(cfg, $item, item.field.value);
let prefix = cfg.column.prefix;
if (!env.mode.search)
{
let fnUpd = function () { as.updateItem(item) };
$mnu.append(as.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.' + prefix + 'Target', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
$('.' + prefix + 'Status', $item).removeStatusClasses().addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
var $bgr = $('.sbm-burger', $item).hide();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
let fnUpd = function () { as.updateItem(env, item.id) };
$mnu.append(as.getItemMenuOption(env, sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.' + prefix + 'Target', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
}
$bgr.toggle(!!$('.sbm-dropdown-item', $mnu).length);
}
else if (env.prmSelect)
{
$item.off('click').on('click', function () { as.selectItemRow(env, $item, item); });
$item.off('dblclick').on('dblclick', function () { as.selectItem(env); });
}
$bgr.toggle(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
as.getItemId = function (item)
@@ -80,27 +107,27 @@
}
//--------------------------------------------------------------
as.getFilterDropdownTemplate = function ()
as.getFilterDropdownTemplate = function (env)
{
return $('.sbm-dropdown-menu.AssociationList li.sbm-dropdown-item', $pnl.tmpls).clone();
return $('.sbm-dropdown-menu.AssociationList li.sbm-dropdown-item', env.$pnl.tmpls).clone();
}
as.getItemTemplate = function ()
as.getItemTemplate = function (env)
{
return $('table tr.sbm-row-item.Association', $pnl.tmpls).clone();
return $('table tr.sbm-row-item.Association', env.$pnl.tmpls).clone();
}
as.getItemDropdownTemplate = function ()
as.getItemDropdownTemplate = function (env)
{
return $('.sbm-dropdown-menu.Association li.sbm-dropdown-item', $pnl.tmpls).clone();
return $('.sbm-dropdown-menu.Association li.sbm-dropdown-item', env.$pnl.tmpls).clone();
}
//--------------------------------------------------------------
as.getFilterMenuOption = function (lbl, fn, cls)
as.getFilterMenuOption = function (env, lbl, fn, cls)
{
return as.getMenuOption(as.getFilterDropdownTemplate(), lbl, fn, cls);
return as.getMenuOption(as.getFilterDropdownTemplate(env), lbl, fn, cls);
}
as.getItemMenuOption = function (lbl, fn, cls)
as.getItemMenuOption = function (env, lbl, fn, cls)
{
return as.getMenuOption(as.getItemDropdownTemplate(), lbl, fn, cls);
return as.getMenuOption(as.getItemDropdownTemplate(env), lbl, fn, cls);
}
as.getMenuOption = function ($opt, lbl, fn, cls)
{
@@ -108,107 +135,217 @@
}
//--------------------------------------------------------------
as.createItem = function (cfg, title)
as.createItem = function (env, cfg, title)
{
as.clearItem(cfg);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', $frm.item);
$ctrls.filter('.AssociationType').val('').prop('disabled', false);
$ctrls.filter('.AssociationStatus').val(as.data.defaultStatusCode);
as.openItemDlg(cfg, title);
as.clearItem(env, cfg);
let prefix = cfg.field.prefix;
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', env.$frm.item);
if (cfg.type) sbm.selectOrAdd($ctrls.filter('.AssociationType'), cfg.type.code, cfg.type.desc)
env.$ctrl.targetActions.filter('.AssocTargetSearch').show();
$ctrls.filter('.AssociationTarget').off('dblclick').on('dblclick', function () { as.selectTarget(env); });
$ctrls.filter('.AssociationStatus').val(env.data.defaultStatusCode);
as.openEditDlg(env, cfg, title);
}
as.clearItem = function (cfg)
as.clearItem = function (env, cfg)
{
sbm.clearItemForm($frm.item);
sbm.populateSelectCodeDesc('#cbAssociationType', as.data.associationTypes);
sbm.populateSelectCodeDesc('#cbAssociationStatus', as.data.statuses);
sbm.clearItemForm(env.$frm.item);
let prefix = cfg.field.prefix;
sbm.populateSelectCodeDesc('#cbAssociationType', env.data.associationTypes);
env.$ctrl.targetActions.hide();
sbm.populateSelectCodeDesc('#cbAssociationStatus', env.data.statuses);
}
as.openItemDlg = function (cfg, title)
as.openEditDlg = function (env, cfg, title)
{
sbm.configureItemControls(cfg, $frm.item);
$dlg.item.dialog('option', 'title', title);
sbm.openDialog($dlg.item, 640, 600);
sbm.configureItemControls(cfg, env.$frm.item);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', env.$frm.item);
$ctrls.filter('.AssociationTarget').prop('disabled', false)
let $dlg = env.$dlg.item;
let btns = {};
btns[sbm.lbl.ok] = function () { as.storeItem(env); };
btns[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.dialog('option', 'buttons', btns);
if (title) $dlg.dialog('option', 'title', title);
sbm.openDialog($dlg, 720, 640);
}
//--------------------------------------------------------------
as.updateItem = function (item)
as.selectTarget = function (env)
{
$('.AssociationId', $frm.itemId).val(item.id);
as.ajax.updateItem = as.ajax.updateItem || { url: sbm.url.base + 'Association/Ajax/GetItem', formId: 'frmAssociationId', callbackFn: as.onUpdateItem };
sbm.doAjaxRequest(as.ajax.updateItem, 50);
sbm.fn.selectContact({ fnSuccess: function (item) { as.onSelectTarget(env, item); } });
}
as.onUpdateItem = function (response)
as.onSelectTarget = function (env, item)
{
//sbm.debug('onUpdateItem', response);
//sbm.debug('onSelectTarget', env, item);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', env.$frm.item);
$ctrls.filter('.AssociationTargetId').val(item.id);
$ctrls.filter('.AssociationTarget').val(item.Name);
}
as.editTarget = function (env)
{
let cfg = {
id: $('.sbm-input', env.$frm.item).filter('.AssociationTargetId').val(),
fnSuccess: function (item) { as.onEditTarget(env, item); }
};
sbm.fn.editContact(cfg);
}
as.onEditTarget = function (env, item)
{
//sbm.debug('onEditTarget', env, item);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', env.$frm.item);
$ctrls.filter('.AssociationTarget').val(item.name);
}
//--------------------------------------------------------------
as.updateItem = function (env, id)
{
let $frm = env.$frm.itemId;
$('.AssociationId', $frm).val(id);
env.ajax.updateItem = env.ajax.updateItem || { url: sbm.url.base + 'Association/Ajax/GetItem', formSel: $frm, callbackFn: function (response) { as.onUpdateItem(env, response); } };
sbm.doAjaxRequest(env.ajax.updateItem, 50);
}
as.onUpdateItem = function (env, response)
{
//sbm.debug('onUpdateItem', env, response);
if (!response.success) return;
let cfg = response.config || {};
let prefix = cfg.field.prefix;
as.clearItem(cfg);
as.clearItem(env, cfg);
let item = response.association;
sbm.populateFormValues(cfg, $frm.item, item.field.value);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', $frm.item);
let $frm = env.$frm.item;
sbm.populateFormValues(cfg, $frm, item.field.value);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', $frm);
$ctrls.filter('.AssociationId').val(item.id);
$ctrls.filter('.AssociationExists').val(1);
let $ctrlType = $ctrls.filter('.AssociationType');
sbm.selectOrAdd($ctrlType, item.typeCode, item.typeDesc)
//if ($ctrlType.val()) $ctrlType.prop('disabled', true);
sbm.selectOrAdd($ctrls.filter('.AssociationType'), item.typeCode, item.typeDesc)
$ctrls.filter('.AssociationOwnerId').val(item.ownerId);
$ctrls.filter('.AssociationTargetId').val(item.targetId);
env.$ctrl.targetActions.filter('.AssocTargetEdit').show();
$ctrls.filter('.AssociationTarget').off('dblclick').on('dblclick', function () { as.editTarget(env); });
sbm.selectOrAdd($ctrls.filter('.AssociationStatus'), item.statusCode, item.statusDesc)
as.openItemDlg(cfg, cfg.title || sbm.dlg.title.updateAssociation);
as.openEditDlg(env, cfg, cfg.title || sbm.dlg.title.updateAssociation);
}
//--------------------------------------------------------------
as.storeItem = function ()
as.storeItem = function (env)
{
as.ajax.storeItem = as.ajax.storeItem || { url: sbm.url.base + 'Association/Ajax/StoreItem', formId: 'frmAssociation', callbackFn: as.onStoreItem };
sbm.doAjaxRequest(as.ajax.storeItem, 50);
env.ajax.storeItem = env.ajax.storeItem || { url: sbm.url.base + 'Association/Ajax/StoreItem', formSel: env.$frm.item, callbackFn: function (response) { as.onStoreItem(env, response); } };
sbm.doAjaxRequest(env.ajax.storeItem, 50);
}
as.onStoreItem = function (response)
as.onStoreItem = function (env, response)
{
//sbm.debug('onStoreItem', response);
//sbm.debug('onStoreItem', env, response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
$pnl.msgs.hide().empty();
env.$dlg.item.dialog('close');
env.$pnl.msgs.hide().empty();
var item = response.association;
var $item = $('#' + as.getItemId(item), $pnl.table);
var $item = $('#' + as.getItemId(item), env.$pnl.table);
if (!$item.length)
{
$item = as.getItemTemplate();
$('table tbody', $pnl.table).prepend($item).show();
$item = as.getItemTemplate(env);
$('table tbody', env.$pnl.table).prepend($item).show();
}
let cfg = response.config || {};
as.populateItem($item, item);
as.populateItem(env, $item, item);
}
//--------------------------------------------------------------
/* Params: ownerId, targetId, search. */
sbm.fn.listAssociations = function (prm)
{
let env = as.env.select;
//sbm.debug('listAssociations', env, prm);
env.prmList = prm;
let $frm = env.$frm.filter;
$frm.find('.FilterOwnerId').val(prm.ownerId);
$frm.find('.FilterTargetId').val(prm.targetId);
$frm.find('.FilterContactType').val('');
$frm.find('.FilterSearch').val(prm.search || '');
env.$pnl.table.find('tbody tr').remove();
let $dlg = env.$dlg.select;
let btns = $dlg.dialog('option', 'buttons');
btns[sbm.lbl.close] = function () { delete env.prmSelect; delete env.prmList; $(this).dialog('close'); };
$dlg.dialog('option', 'buttons', btns);
sbm.openDialog($dlg, 1000, 800);
if (env.$pnl.items.length) setTimeout(function () { as.refreshResults(env); }, 250);
}
/* Params: type, search. */
sbm.fn.selectAssociation = function (prm)
{
let env = as.env.select;
//sbm.debug('listAssociations', env, prm);
env.prmSelect = prm;
let $frm = env.$frm.filter;
$frm.find('.FilterOwnerId,.FilterTargetId').val('');
$frm.find('.FilterContactType').val(prm.type || '');
$frm.find('.FilterSearch').val(prm.search || '');
env.$pnl.table.find('tbody tr').remove();
let $dlg = env.$dlg.select;
let btns = $dlg.dialog('option', 'buttons');
btns[sbm.lbl.ok] = function () { as.selectItem(env); };
btns[sbm.lbl.cancel] = function () { delete env.prmSelect; delete env.prmList; $(this).dialog('close'); };
$dlg.dialog('option', 'buttons', btns);
sbm.openDialog($dlg, 1000, 800);
if (env.$pnl.items.length) setTimeout(function () { as.refreshResults(env); }, 250);
}
as.selectItemRow = function (env, $item, item)
{
//sbm.debug('selectItemRow', env, $item, item);
env.$pnl.table.find('tr').removeClass('sbm-sel');
$item.addClass('sbm-sel');
env.prmSelect.item = item;
}
as.selectItem = function (env)
{
//sbm.debug('selectItem', env, env.prmSelect.item);
env.$dlg.select.dialog('close');
env.prmSelect.fnSuccess(env.prmSelect.item);
delete env.prmSelect;
}
//--------------------------------------------------------------
as.initContactEnv = function ($pnlItems)
{
let env = { data: {}, ajax: {}, $pnl: {}, $frm: {}, $ctrl: {}, $dlg: {} };
env.$pnl.items = $($pnlItems);
env.$pnl.header = env.$pnl.items.find('.sbm-panel-header');
env.$pnl.bgr = env.$pnl.header.find('.sbm-burger');
env.$pnl.mnu = env.$pnl.bgr.find('ul.sbm-dropdown-menu');
env.$pnl.table = env.$pnl.items.find('.sbm-panel-item-table');
env.$pnl.msgs = env.$pnl.table.find('.sbm-panel-messages');
env.$pnl.tmpls = env.$pnl.items.find('.sbm-panel-item-template');
env.$frm.filter = env.$pnl.items.find('.frmAssociationFilter');
env.$frm.filter.on('input', '.FilterSearch', function () { as.refreshResults(env, 250); }).on('change', '.FilterAssociationType', function () { as.refreshResults(env, 50); });
var opt = sbm.dlgOpts();
opt.dialogClass += " sbm-Association";
env.$dlg.item = $('#dlgAssociation').dialog(opt);
env.$frm.itemId = $('#frmAssociationId');
env.$frm.item = $('#frmAssociation').on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
env.$ctrl.targetActions = env.$frm.item.find('.AssocTarget .sbm-control .sbm-action');
env.$ctrl.targetActions.filter('.AssocTargetSearch').off('click').on('click', function () { as.selectTarget(env); });
env.$ctrl.targetActions.filter('.AssocTargetEdit').off('click').on('click', function () { as.editTarget(env); });
var opt = sbm.dlgOpts();
opt.dialogClass += " sbm-Association";
env.$dlg.select = $('#dlgAssociationSelect').dialog(opt);
return env;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.Associations');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.bgr = $('.sbm-burger', $pnl.header);
$pnl.mnu = $('ul.sbm-dropdown-menu', $pnl.bgr);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.table);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmAssociationFilter');
$frm.filter.on('input', '.FilterSearch', function () { as.refreshResults(250); }).on('change', '.FilterAssociationType', function () { as.refreshResults(50); });
$frm.itemId = $('#frmAssociationId');
$frm.item = $('#frmAssociation');
$frm.item.on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.ok] = function () { as.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgAssociation').dialog(opt);
setTimeout(as.refreshResults, 250);
as.env.maint = as.initContactEnv('.sbm-panel.Associations');
if (as.env.maint.$pnl.items.length) setTimeout(function () { as.refreshResults(as.env.maint); }, 250);
as.env.select = as.initContactEnv('#dlgAssociationSelect');
if (as.env.select.$pnl.items.length) setTimeout(function () { as.refreshConfig(as.env.select); }, 250);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */
@@ -5,79 +5,104 @@
(function (win, doc, undef)
{
"use strict";
let co = sbm.Contacts = { data: {}, ajax: {} };
let $pnl = co.$pnl = {}, $frm = co.$frm = {}, $dlg = co.$dlg = {};
let co = sbm.mdl.Contacts = { env: {} };
//--------------------------------------------------------------
co.refreshResults = function (delay)
co.refreshConfig = function (env, delay)
{
if (!sbm.awaitingRefreshResponse)
{
$pnl.bgr.invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
co.ajax.refreshResults = co.ajax.refreshResults || { url: sbm.url.base + 'Contact/Ajax/List', formId: $frm.filter.prop('id'), callbackFn: co.doRefreshResults };
sbm.doAjaxRequest(co.ajax.refreshResults, delay || 50);
sbm.awaitingRefreshResponse = true;
env.ajax.refreshConfig = env.ajax.refreshConfig || { url: sbm.url.base + 'Contact/Ajax/Config', formSel: env.$frm.itemId, callbackFn: function (response) { co.doRefreshConfig(env, response); } };
sbm.doAjaxRequest(env.ajax.refreshConfig, delay || 50);
}
co.doRefreshResults = function (response)
co.doRefreshConfig = function (env, response)
{
//sbm.debug('doRefreshResults', response);
delete sbm.awaitingRefreshResponse;
$pnl.msgs.hide().empty();
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
sbm.refreshPaging(response, '#frmContactFilter', co.refreshResults);
//sbm.debug('doRefreshConfig', env, response);
if (!response.success) return;
// Config.
env.mode = response.mode;
env.config = response.config;
env.configInd = response.configInd;
env.configOrg = response.configOrg;
env.configGrp = response.configGrp;
env.data.organisationTypes = response.organisationTypes;
env.data.titles = response.titles;
env.data.salutations = response.salutations;
env.data.statuses = response.statuses;
env.data.defaultStatusCode = response.defaultStatusCode;
}
// Reference data.
co.config = response.config;
co.configInd = response.configInd;
co.configOrg = response.configOrg;
co.configGrp = response.configGrp;
co.data.organisationTypes = response.organisationTypes;
co.data.titles = response.titles;
co.data.salutations = response.salutations;
co.data.statuses = response.statuses;
co.data.defaultStatusCode = response.defaultStatusCode;
//--------------------------------------------------------------
co.refreshResults = function (env, delay)
{
if (!co.awaitingRefreshResponse)
{
env.$pnl.bgr.invisible();
env.$pnl.table.find('tbody tr').remove();
env.$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
env.ajax.refreshResults = env.ajax.refreshResults || { url: sbm.url.base + 'Contact/Ajax/List', formSel: env.$frm.filter, callbackFn: function (response) { co.doRefreshResults(env, response); } };
sbm.doAjaxRequest(env.ajax.refreshResults, delay || 50);
co.awaitingRefreshResponse = true;
}
co.doRefreshResults = function (env, response)
{
//sbm.debug('doRefreshResults', env, response);
delete co.awaitingRefreshResponse;
env.$pnl.msgs.hide().empty();
let $table = $('table', env.$pnl.table).find('tbody tr').remove().end();
sbm.refreshPaging(response, env.$frm.filter, function () { co.refreshResults(env); });
if (!response.success) return;
co.doRefreshConfig(env, response);
// Menu.
$pnl.bgr.invisible();
$pnl.mnu.empty();
$pnl.mnu.append(co.getFilterMenuOption(sbm.lbl.refresh, function () { co.refreshResults(); }, 'sbm-refresh'));
$pnl.mnu.append(co.getFilterMenuOption(sbm.lbl.createOrg, function () { co.createItem(co.configOrg, sbm.dlg.title.createContact); }, 'sbm-add'));
$pnl.mnu.append(co.getFilterMenuOption(sbm.lbl.createInd, function () { co.createItem(co.configInd, sbm.dlg.title.createContact); }, 'sbm-add'));
$pnl.mnu.append(co.getFilterMenuOption(sbm.lbl.createGrp, function () { co.createItem(co.configGrp, sbm.dlg.title.createContact); }, 'sbm-add'));
$pnl.bgr.toggleVisibility(!!$('.sbm-dropdown-item', $pnl.mnu).length);
if (env.$pnl.bgr.length)
{
env.$pnl.bgr.invisible();
env.$pnl.mnu.empty();
env.$pnl.mnu.append(co.getFilterMenuOption(env, sbm.lbl.refresh, function () { co.refreshResults(env); }, 'sbm-refresh'));
env.$pnl.mnu.append(co.getFilterMenuOption(env, sbm.lbl.createOrg, function () { co.createItem(env, env.configOrg, sbm.dlg.title.createContact); }, 'sbm-add'));
env.$pnl.mnu.append(co.getFilterMenuOption(env, sbm.lbl.createInd, function () { co.createItem(env, env.configInd, sbm.dlg.title.createContact); }, 'sbm-add'));
env.$pnl.mnu.append(co.getFilterMenuOption(env, sbm.lbl.createGrp, function () { co.createItem(env, env.configGrp, sbm.dlg.title.createContact); }, 'sbm-add'));
env.$pnl.bgr.toggleVisibility(!!$('.sbm-dropdown-item', env.$pnl.mnu).length);
}
// Items.
var items = response.contacts || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
return env.$pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(co.populateItem(co.getItemTemplate(), items[itemIdx]));
sbm.configureItemTable(co.config, $table);
$table.append(co.populateItem(env, co.getItemTemplate(env), items[itemIdx]));
sbm.configureItemTable(env.config, $table);
}
co.populateItem = function ($item, item)
co.populateItem = function (env, $item, item)
{
if (!item) return;
$item.prop('id', co.getItemId(item));
sbm.populateItemValues(co.config, $item, item.field.value);
let prefix = co.config.column.prefix;
if (item.associationCount)
$('.' + prefix + 'Associations', $item).text(item.associationCount).off('click').on('click', function () { co.onClickAssociationCount(item); }).addClass('sbm-action-text');
$('.' + prefix + 'Status', $item).removeStatusClasses().addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
var $bgr = $('.sbm-burger', $item).hide();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
let cfg = env.config;
if (cfg) sbm.populateItemValues(cfg, $item, item.field.value);
if (!env.mode.search)
{
let fnUpd = function () { co.updateItem(item) };
$mnu.append(co.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.' + prefix + 'Name', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
let prefix = cfg.column.prefix;
let $assocs = $('.' + prefix + 'Associations', $item).text(item.associationCount || '').off('click');
if (item.associationCount)
$assocs.on('click', function () { co.onClickAssociationCount(env, item); }).addClass('sbm-action-text');
$('.' + prefix + 'Status', $item).removeStatusClasses().addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
var $bgr = $('.sbm-burger', $item).hide();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
let fnUpd = function () { co.updateItem(env, item.id) };
$mnu.append(co.getItemMenuOption(env, sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.' + prefix + 'Name', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
}
$bgr.toggle(!!$('.sbm-dropdown-item', $mnu).length);
}
else
{
$item.off('click').on('click', function () { co.selectItemRow(env, $item, item); });
$item.off('dblclick').on('dblclick', function () { co.selectItem(env); });
}
$bgr.toggle(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
co.getItemId = function (item)
@@ -86,27 +111,27 @@
}
//--------------------------------------------------------------
co.getFilterDropdownTemplate = function ()
co.getFilterDropdownTemplate = function (env)
{
return $('.sbm-dropdown-menu.ContactList li.sbm-dropdown-item', $pnl.tmpls).clone();
return $('.sbm-dropdown-menu.ContactList li.sbm-dropdown-item', env.$pnl.tmpls).clone();
}
co.getItemTemplate = function ()
co.getItemTemplate = function (env)
{
return $('table tr.sbm-row-item.Contact', $pnl.tmpls).clone();
return $('table tr.sbm-row-item.Contact', env.$pnl.tmpls).clone();
}
co.getItemDropdownTemplate = function ()
co.getItemDropdownTemplate = function (env)
{
return $('.sbm-dropdown-menu.Contact li.sbm-dropdown-item', $pnl.tmpls).clone();
return $('.sbm-dropdown-menu.Contact li.sbm-dropdown-item', env.$pnl.tmpls).clone();
}
//--------------------------------------------------------------
co.getFilterMenuOption = function (lbl, fn, cls)
co.getFilterMenuOption = function (env, lbl, fn, cls)
{
return co.getMenuOption(co.getFilterDropdownTemplate(), lbl, fn, cls);
return co.getMenuOption(co.getFilterDropdownTemplate(env), lbl, fn, cls);
}
co.getItemMenuOption = function (lbl, fn, cls)
co.getItemMenuOption = function (env, lbl, fn, cls)
{
return co.getMenuOption(co.getItemDropdownTemplate(), lbl, fn, cls);
return co.getMenuOption(co.getItemDropdownTemplate(env), lbl, fn, cls);
}
co.getMenuOption = function ($opt, lbl, fn, cls)
{
@@ -114,115 +139,183 @@
}
//--------------------------------------------------------------
co.createItem = function (cfg, title)
co.createItem = function (env, cfg, title)
{
co.clearItem(cfg);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', $frm.item);
$ctrls.filter('.ContactStatus').val(co.data.defaultStatusCode);
co.openItemDlg(cfg, title);
co.clearItem(env, cfg);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select', env.$frm.item);
$ctrls.filter('.ContactStatus').val(env.data.defaultStatusCode);
co.openEditDlg(env, cfg, title);
}
co.clearItem = function (cfg)
co.clearItem = function (env, cfg)
{
//sbm.debug('clearItem', cfg);
let frm = $frm.item;
sbm.clearItemForm(frm);
if (cfg.isIndividual) $('.ContactIsIndividual', frm).val(1);
if (cfg.isGroup) $('.ContactIsGroup', frm).val(1);
sbm.populateSelectCodeDesc('#cbContactType', co.data.organisationTypes);
sbm.populateSelectCodeDesc('#cbContactTitle', co.data.titles);
sbm.populateSelectCodeDesc('#cbContactSalutation', co.data.salutations);
sbm.populateSelectCodeDesc('#cbContactStatus', co.data.statuses);
//sbm.debug('clearItem', env, cfg);
let $frm = env.$frm.item;
sbm.clearItemForm($frm);
if (cfg.isIndividual) $('.ContactIsIndividual', $frm).val(1);
if (cfg.isGroup) $('.ContactIsGroup', $frm).val(1);
sbm.populateSelectCodeDesc('#cbContactType', env.data.organisationTypes);
sbm.populateSelectCodeDesc('#cbContactTitle', env.data.titles);
sbm.populateSelectCodeDesc('#cbContactSalutation', env.data.salutations);
sbm.populateSelectCodeDesc('#cbContactStatus', env.data.statuses);
}
co.openItemDlg = function (cfg, title)
co.openEditDlg = function (env, cfg, title)
{
sbm.configureItemControls(cfg, $frm.item);
$dlg.item.dialog('option', 'title', title);
sbm.openDialog($dlg.item, 640, 600);
let $frm = env.$frm.item;
sbm.configureItemControls(cfg, $frm);
var btns = {};
btns[sbm.lbl.ok] = function () { co.storeItem(env); };
btns[sbm.lbl.cancel] = function () { delete env.prmEdit; $(this).dialog('close'); };
env.$dlg.item.dialog('option', 'buttons', btns);
if (title) env.$dlg.item.dialog('option', 'title', title);
sbm.openDialog(env.$dlg.item, 640, 600);
}
//--------------------------------------------------------------
co.updateItem = function (item)
co.updateItem = function (env, id)
{
$('.ContactId', $frm.itemId).val(item.id);
co.ajax.updateItem = co.ajax.updateItem || { url: sbm.url.base + 'Contact/Ajax/GetItem', formId: 'frmContactId', callbackFn: co.onUpdateItem };
sbm.doAjaxRequest(co.ajax.updateItem, 50);
let $frm = env.$frm.itemId;
$('.ContactId', $frm).val(id);
env.ajax.updateItem = env.ajax.updateItem || { url: sbm.url.base + 'Contact/Ajax/GetItem', formSel: $frm, callbackFn: function (response) { co.onUpdateItem(env, response); } };
sbm.doAjaxRequest(env.ajax.updateItem, 50);
}
co.onUpdateItem = function (response)
co.onUpdateItem = function (env, response)
{
//sbm.debug('onUpdateItem', response);
//sbm.debug('onUpdateItem', env, response);
if (!response.success) return;
let cfg = response.config || {};
co.clearItem(cfg);
co.clearItem(env, cfg);
let item = response.contact;
sbm.populateFormValues(cfg, $frm.item, item.field.value);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select,.sbm-checkbox', $frm.item);
let $frm = env.$frm.item;
sbm.populateFormValues(cfg, $frm, item.field.value);
let $ctrls = $('.sbm-input,.sbm-textarea,.sbm-select,.sbm-checkbox', $frm);
$ctrls.filter('.ContactId').val(item.id);
$ctrls.filter('.ContactExists').val(1);
$ctrls.filter('.ContactType').val(item.typeCode);
$ctrls.filter('.ContactProspect').prop('checked', !!item.prospect);
$ctrls.filter('.ContactStatus').val(item.statusCode);
co.openItemDlg(cfg, sbm.dlg.title.updateContact);
co.openEditDlg(env, cfg, sbm.dlg.title.updateContact);
}
//--------------------------------------------------------------
co.storeItem = function ()
co.storeItem = function (env)
{
co.ajax.storeItem = co.ajax.storeItem || { url: sbm.url.base + 'Contact/Ajax/StoreItem', formId: 'frmContact', callbackFn: co.onStoreItem };
sbm.doAjaxRequest(co.ajax.storeItem, 50);
env.ajax.storeItem = env.ajax.storeItem || { url: sbm.url.base + 'Contact/Ajax/StoreItem', formSel: env.$frm.item, callbackFn: function (response) { co.onStoreItem(env, response); } };
sbm.doAjaxRequest(env.ajax.storeItem, 50);
}
co.onStoreItem = function (response)
co.onStoreItem = function (env, response)
{
//sbm.debug('onStoreItem', response);
//sbm.debug('onStoreItem', env, response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
$pnl.msgs.hide().empty();
env.$dlg.item.dialog('close');
var item = response.contact;
var $item = $('#' + co.getItemId(item), $pnl.table);
if (!$item.length)
if (env.prmEdit)
{
$item = co.getItemTemplate();
$('table tbody', $pnl.table).prepend($item).show();
co.onContactEdited(env, item);
}
else if (env.$pnl.table.length)
{
env.$pnl.msgs.hide().empty();
var $item = $('#' + co.getItemId(item), env.$pnl.table);
if (!$item.length)
{
$item = co.getItemTemplate(env);
$('table tbody', env.$pnl.table).prepend($item).show();
}
co.populateItem(env, $item, item);
}
co.populateItem($item, item);
}
//--------------------------------------------------------------
co.onClickAssociationCount = function (item)
co.onClickAssociationCount = function (env, item)
{
sbm.debug('onClickAssociationCount');
//sbm.debug('onClickAssociationCount', env, item);
if (!item.id) return;
//sbm.doPleaseWait();
//let frm = $frm.itemId;
//$('.ContactId', frm).val(item.id);
//frm.prop('action', sbm.url.base + 'Association').submit();
sbm.fn.listAssociations({ ownerId: item.id });
}
//--------------------------------------------------------------
/* Params: type, search, fnSuccess. */
sbm.fn.selectContact = function (prm)
{
//sbm.debug('selectContact', prm);
let env = co.env.select;
env.prmSelect = prm;
env.$frm.filter.find('.FilterContactType').val('');
env.$frm.filter.find('.FilterSearch').val(prm.search || '');
env.$pnl.table.find('tbody tr').remove();
sbm.openDialog(env.$dlg.select, 1000, 800);
if (env.$pnl.items.length) setTimeout(function () { co.refreshResults(env); }, 250);
}
co.selectItemRow = function (env, $item, item)
{
//sbm.debug('selectItemRow', env, $item, item);
env.$pnl.table.find('tr').removeClass('sbm-sel');
$item.addClass('sbm-sel');
env.prmSelect.item = item;
}
co.selectItem = function (env)
{
//sbm.debug('selectItem', env, env.prmSelect.item);
env.$dlg.select.dialog('close');
env.prmSelect.fnSuccess(env.prmSelect.item);
delete env.prmSelect;
}
/* Params: id, fnSuccess. */
sbm.fn.editContact = function (prm)
{
//sbm.debug('editContact', prm);
let env = co.env.select;
env.prmEdit = prm;
sbm.debug(env, prm);
co.updateItem(env, prm.id);
}
co.onContactEdited = function (env, item)
{
//sbm.debug('onContactEdited', env, item);
env.prmEdit.fnSuccess(item);
delete env.prmEdit;
}
//--------------------------------------------------------------
co.initContactEnv = function ($pnlItems)
{
let env = { data: {}, ajax: {}, $pnl: {}, $frm: {}, $ctrl: {}, $dlg: {} };
env.$pnl.items = $($pnlItems);
env.$pnl.header = env.$pnl.items.find('.sbm-panel-header');
env.$pnl.bgr = env.$pnl.header.find('.sbm-burger');
env.$pnl.mnu = env.$pnl.bgr.find('ul.sbm-dropdown-menu');
env.$pnl.table = env.$pnl.items.find('.sbm-panel-item-table');
env.$pnl.msgs = env.$pnl.table.find('.sbm-panel-messages');
env.$pnl.tmpls = env.$pnl.items.find('.sbm-panel-item-template');
env.$frm.filter = env.$pnl.items.find('.frmContactFilter');
env.$frm.filter.on('input', '.FilterSearch', function () { co.refreshResults(env, 250); }).on('change', '.FilterContactType', function () { co.refreshResults(env, 50); });
var opt = sbm.dlgOpts();
opt.dialogClass += " sbm-Contact";
env.$dlg.item = $('#dlgContact').dialog(opt);
env.$frm.itemId = $('#frmContactId');
env.$frm.item = $('#frmContact').on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
var opt = sbm.dlgOpts();
opt.dialogClass += " sbm-Contact";
opt.buttons[sbm.lbl.ok] = function () { co.selectItem(env); };
opt.buttons[sbm.lbl.cancel] = function () { delete env.prmSelect; $(this).dialog('close'); };
env.$dlg.select = $('#dlgContactSelect').dialog(opt);
return env;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.Contacts');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.bgr = $('.sbm-burger', $pnl.header);
$pnl.mnu = $('ul.sbm-dropdown-menu', $pnl.bgr);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.table);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmContactFilter', $pnl.items);
$frm.filter.on('input', '.FilterSearch', function () { co.refreshResults(250); }).on('change', '.FilterContactType', function () { co.refreshResults(50); });
$frm.itemId = $('#frmContactId');
$frm.item = $('#frmContact');
$frm.item.on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.ok] = function () { co.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgContact').dialog(opt);
setTimeout(co.refreshResults, 250);
co.env.maint = co.initContactEnv('.sbm-panel.Contacts');
if (co.env.maint.$pnl.items.length) setTimeout(function () { co.refreshResults(co.env.maint); }, 250);
co.env.select = co.initContactEnv('#dlgContactSelect');
co.env.select.$frm.filter.find('.FilterContactType option[value="GRP"]').remove();
if (co.env.select.$pnl.items.length) setTimeout(function () { co.refreshConfig(co.env.select); }, 250);
});
//--------------------------------------------------------------
@@ -150,7 +150,7 @@
}
sbm.refreshPaging = function (response, context, refreshFn)
{
if (sbm.resultsPaging.fn.refreshPaging)
if (sbm.isFn(sbm.resultsPaging.fn.refreshPaging))
sbm.resultsPaging.fn.refreshPaging(response.paging, context, refreshFn);
}
@@ -366,7 +366,7 @@
type: 'POST',
cache: false,
//enctype: 'multipart/form-data',
data: ajax.formData || $("#" + ajax.formId).serialize(),
data: ajax.formData || $(ajax.formSel || "#" + ajax.formId).serialize(),
dataType: 'json',
success: function (response, status, jqXHR)
{
@@ -438,8 +438,7 @@
}
sbm.triggeRefreshSession = function ()
{
sbm.session = sbm.session || { refresh: {} };
let ssr = sbm.session.refresh;
let ssr = sbm.session.refresh = sbm.session.refresh || {};
win.clearTimeout(ssr.timeout);
ssr.timeout = setTimeout(function () { sbm.refreshSession(); }, 150000);
}
@@ -10,6 +10,7 @@
//--------------------------------------------------------------
rp.fn.refreshPaging = function (paging, contextSel, refreshFn)
{
//sbm.debug('refreshPaging', paging, contextSel, refreshFn);
let context = rp.fn.getContext(contextSel, refreshFn);
rp.fn.init(context);
context.paging = paging = paging || {};