diff --git a/zero.Backoffice.UI/app/components/ui-table-filter.vue b/zero.Backoffice.UI/app/components/ui-table-filter.vue index 8180dee1..9c58e148 100644 --- a/zero.Backoffice.UI/app/components/ui-table-filter.vue +++ b/zero.Backoffice.UI/app/components/ui-table-filter.vue @@ -108,7 +108,7 @@ // called when an action has been clicked onActionClicked(action, opts) { - action.call(opts); + action.call(opts, this); }, diff --git a/zero.Backoffice.UI/app/components/ui-table.scss b/zero.Backoffice.UI/app/components/ui-table.scss index 347a4f22..5807f76b 100644 --- a/zero.Backoffice.UI/app/components/ui-table.scss +++ b/zero.Backoffice.UI/app/components/ui-table.scss @@ -294,16 +294,17 @@ a.ui-table-row:hover, button.ui-table-row:hover padding: 4px 12px; border-radius: 20px; - &[data-state="completed"], &[data-state="cancelled"] + &[data-state="completed"], &[data-state="cancelled"], + &[data-state="none"], &[data-state="captured"], &[data-state="authorized"], &[data-state="cancelled"], &[data-state="refunded"] { font-weight: 400; color: var(--color-text-dim); } - /*&[data-payment-state="none"], &[data-payment-state="pending"], &[data-payment-state="cancelled"], &[data-payment-state="refunded"] + &[data-state="error"] { - font-weight: 400; - color: var(--color-text-dim); - }*/ + color: var(--color-accent-error); + background: var(--color-accent-error-bg); + } &[data-payment-state] { font-weight: 400; diff --git a/zero.Backoffice.UI/app/schemas/list/list-action.ts b/zero.Backoffice.UI/app/schemas/list/list-action.ts index 2d47ece7..fc72beb9 100644 --- a/zero.Backoffice.UI/app/schemas/list/list-action.ts +++ b/zero.Backoffice.UI/app/schemas/list/list-action.ts @@ -23,9 +23,9 @@ class ListAction * Calls the action * @returns {ListColumn} */ - call(options) + call(options, cmp) { - this.action(options); + this.action(options, cmp); } } diff --git a/zero.Core/Persistence/Tokens/ZeroTokenProvider.cs b/zero.Core/Persistence/Tokens/ZeroTokenProvider.cs index b7bb0470..221e2ca9 100644 --- a/zero.Core/Persistence/Tokens/ZeroTokenProvider.cs +++ b/zero.Core/Persistence/Tokens/ZeroTokenProvider.cs @@ -49,8 +49,7 @@ public class ZeroTokenProvider : IZeroTokenProvider await session.StoreAsync(securityToken); // set the expires flag for the token - IMetadataDictionary tokenMetadata = session.Advanced.GetMetadataFor(securityToken); - tokenMetadata[Constants.Database.Expires] = DateTime.UtcNow.AddSeconds(expires.TotalSeconds); + session.Expires(securityToken, expires); await session.SaveChangesAsync(); return tokenKey; diff --git a/zero.Core/Persistence/ZeroDocumentSessionExtensions.cs b/zero.Core/Persistence/ZeroDocumentSessionExtensions.cs new file mode 100644 index 00000000..bf923c30 --- /dev/null +++ b/zero.Core/Persistence/ZeroDocumentSessionExtensions.cs @@ -0,0 +1,14 @@ +namespace zero.Persistence; + +public static class ZeroDocumentSessionExtensions +{ + public static void SetCollection(this IZeroDocumentSession session, T model, string collectionName) + { + session.Advanced.GetMetadataFor(model)[Raven.Client.Constants.Documents.Metadata.Collection] = collectionName; + } + + public static void Expires(this IZeroDocumentSession session, T model, TimeSpan expires) + { + session.Advanced.GetMetadataFor(model)[Raven.Client.Constants.Documents.Metadata.Expires] = DateTime.UtcNow.AddSeconds(expires.TotalSeconds); + } +} diff --git a/zero.Core/Stores/EntityStore.cs b/zero.Core/Stores/EntityStore.cs index 82082a76..165a8b2d 100644 --- a/zero.Core/Stores/EntityStore.cs +++ b/zero.Core/Stores/EntityStore.cs @@ -67,10 +67,10 @@ public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, I public virtual string GetChangeToken(T model) => Operations.GetChangeToken(model); /// - public virtual Task> Create(T model) => Operations.Create(model, async m => await Validate(m)); + public virtual Task> Create(T model, Action onAfterStore = null) => Operations.Create(model, async m => await Validate(m), onAfterStore); /// - public virtual Task> Update(T model) => Operations.Update(model, async m => await Validate(m)); + public virtual Task> Update(T model, Action onAfterStore = null) => Operations.Update(model, async m => await Validate(m), onAfterStore); /// public virtual Task>> Sort(string[] sortedIds) => Operations.Sort(sortedIds); @@ -171,12 +171,12 @@ public interface IEntityStore where T : ZeroIdEntity, ISupportsFlavors, ISupp /// /// Creates an entity with an optional validator /// - Task> Create(T model); + Task> Create(T model, Action onAfterStore = null); /// /// Updates an entity with an optional validator /// - Task> Update(T model); + Task> Update(T model, Action onAfterStore = null); /// /// Update sorting of entities on a specific level diff --git a/zero.Core/Stores/StoreOperations.Delete.cs b/zero.Core/Stores/StoreOperations.Delete.cs index 93af4ccf..465d23c8 100644 --- a/zero.Core/Stores/StoreOperations.Delete.cs +++ b/zero.Core/Stores/StoreOperations.Delete.cs @@ -12,14 +12,17 @@ public partial class StoreOperations : IStoreOperations InterceptorInstruction instruction = Interceptors.ForDelete(model); - if (!await instruction.Start(this)) + if (InterceptorBlocker == null && !await instruction.Start(this)) { return instruction.Result; } Session.Delete(model); await Session.SaveChangesAsync(); - await instruction.Complete(); + if (InterceptorBlocker == null) + { + await instruction.Complete(); + } await Session.SaveChangesAsync(); return Result.Success(); diff --git a/zero.Core/Stores/StoreOperations.Write.cs b/zero.Core/Stores/StoreOperations.Write.cs index bf864eff..9744c75e 100644 --- a/zero.Core/Stores/StoreOperations.Write.cs +++ b/zero.Core/Stores/StoreOperations.Write.cs @@ -5,13 +5,13 @@ namespace zero.Stores; public partial class StoreOperations : IStoreOperations { /// - public virtual Task> Create(T model, Func> validate = null) where T : ZeroIdEntity, new() => Save(model, validate); + public virtual Task> Create(T model, Func> validate = null, Action onAfterStore = null) where T : ZeroIdEntity, new() => Save(model, validate, onAfterStore); /// - public virtual Task> Update(T model, Func> validate = null) where T : ZeroIdEntity, new() => Save(model, validate); + public virtual Task> Update(T model, Func> validate = null, Action onAfterStore = null) where T : ZeroIdEntity, new() => Save(model, validate, onAfterStore); /// - protected virtual async Task> Save(T model, Func> validate = null) where T : ZeroIdEntity, new() + protected virtual async Task> Save(T model, Func> validate = null, Action onAfterStore = null) where T : ZeroIdEntity, new() { if (model == null) { @@ -69,15 +69,19 @@ public partial class StoreOperations : IStoreOperations // run interceptor InterceptorInstruction instruction = isUpdate ? Interceptors.ForUpdate(model, previousModel) : Interceptors.ForCreate(model); - if (!await instruction.Start(this)) + if (InterceptorBlocker == null && !await instruction.Start(this)) { return instruction.Result; } // store our model await Session.StoreAsync(model); + onAfterStore?.Invoke(Session); await Session.SaveChangesAsync(); - await instruction.Complete(); + if (InterceptorBlocker == null) + { + await instruction.Complete(); + } await Session.SaveChangesAsync(); return Result.Success(model); diff --git a/zero.Core/Stores/StoreOperations.cs b/zero.Core/Stores/StoreOperations.cs index ed91d50a..2ecdf84b 100644 --- a/zero.Core/Stores/StoreOperations.cs +++ b/zero.Core/Stores/StoreOperations.cs @@ -28,6 +28,8 @@ public partial class StoreOperations : protected IServiceProvider Services { get; private set; } + protected StoreInterceptorBlocker InterceptorBlocker { get; private set; } + string _overrideDatabase = null; @@ -98,9 +100,7 @@ public partial class StoreOperations : } - /// - /// Validates an entity - /// + /// public async Task Validate(T model) where T : ZeroIdEntity, new() { IZeroMergedValidator validator = Services.GetService>(); @@ -114,6 +114,13 @@ public partial class StoreOperations : } + /// + public StoreInterceptorBlocker WithoutInterceptors() + { + return InterceptorBlocker ?? (InterceptorBlocker = new(() => InterceptorBlocker = null)); + } + + /// /// Do only return the model when it is set to active or inactive entities are included with IncludeInactive() /// @@ -124,6 +131,21 @@ public partial class StoreOperations : } +public class StoreInterceptorBlocker : IDisposable +{ + Action _onRelease; + + internal StoreInterceptorBlocker(Action onRelease) + { + _onRelease = onRelease; + } + + public void Dispose() + { + _onRelease(); + } +} + public interface IStoreOperations { @@ -211,15 +233,20 @@ public interface IStoreOperations /// Task Validate(T model) where T : ZeroIdEntity, new(); + /// + /// Do not run interceptors for create/update/delete operations while this disposable is active + /// + StoreInterceptorBlocker WithoutInterceptors(); + /// /// Creates an entity with an optional validator /// - Task> Create(T model, Func> validate = null) where T : ZeroIdEntity, new(); + Task> Create(T model, Func> validate = null, Action onAfterStore = null) where T : ZeroIdEntity, new(); /// /// Updates an entity with an optional validator /// - Task> Update(T model, Func> validate = null) where T : ZeroIdEntity, new(); + Task> Update(T model, Func> validate = null, Action onAfterStore = null) where T : ZeroIdEntity, new(); /// Task>> Sort(string[] sortedIds) where T : ZeroIdEntity, ISupportsSorting, new(); diff --git a/zero.Core/Stores/TreeEntityStore.cs b/zero.Core/Stores/TreeEntityStore.cs index e153277e..45ef40ee 100644 --- a/zero.Core/Stores/TreeEntityStore.cs +++ b/zero.Core/Stores/TreeEntityStore.cs @@ -9,23 +9,23 @@ public abstract class TreeEntityStore : EntityStore, ITreeEntityStore w /// - public override async Task> Create(T model) + public override async Task> Create(T model, Action onAfterStore = null) { if (!await IsAllowedAsChild(model, model.ParentId)) { return Result.Fail("@errors.treeentity.parentnotallowed"); } - return await base.Create(model); + return await base.Create(model, onAfterStore); } /// - public override async Task> Update(T model) + public override async Task> Update(T model, Action onAfterStore = null) { if (!await IsAllowedAsChild(model, model.ParentId)) { return Result.Fail("@errors.treeentity.parentnotallowed"); } - return await base.Update(model); + return await base.Update(model, onAfterStore); } ///