diff --git a/server/AyaNova/Controllers/DashboardViewController.cs b/server/AyaNova/Controllers/DashboardViewController.cs
new file mode 100644
index 00000000..cda0d591
--- /dev/null
+++ b/server/AyaNova/Controllers/DashboardViewController.cs
@@ -0,0 +1,123 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Authorization;
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+using AyaNova.Models;
+using AyaNova.Api.ControllerHelpers;
+using AyaNova.Biz;
+
+
+namespace AyaNova.Api.Controllers
+{
+
+ ///
+ ///
+ ///
+ [ApiController]
+ [ApiVersion("8.0")]
+ [Route("api/v{version:apiVersion}/dashboard-view")]
+ [Produces("application/json")]
+ [Authorize]
+ public class DashboardViewController : ControllerBase
+ {
+ private readonly AyContext ct;
+ private readonly ILogger log;
+ private readonly ApiServerState serverState;
+
+
+ ///
+ /// ctor
+ ///
+ ///
+ ///
+ ///
+ public DashboardViewController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState)
+ {
+ ct = dbcontext;
+ log = logger;
+ serverState = apiServerState;
+ }
+
+
+ ///
+ /// Get full DashboardView object
+ ///
+ ///
+ /// A single DashboardView
+ [HttpGet("{id}")]
+ public async Task GetDashboardView([FromRoute] long id)
+ {
+ if (!serverState.IsOpen)
+ return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+
+ //Instantiate the business object handler
+ DashboardViewBiz biz = DashboardViewBiz.GetBiz(ct, HttpContext);
+
+ if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
+ return StatusCode(403, new ApiNotAuthorizedResponse());
+
+ if (!ModelState.IsValid)
+ return BadRequest(new ApiErrorResponse(ModelState));
+
+ var o = await biz.GetAsync();
+ if (o == null)
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+
+ return Ok(ApiOkResponse.Response(o));
+ }
+
+
+
+
+ ///
+ /// Put (update) DashboardView
+ ///
+ ///
+ ///
+ ///
+ [HttpPut("{id}")]
+ public async Task PutDashboardView([FromRoute] long id, [FromBody] DashboardView inObj)
+ {
+ if (!serverState.IsOpen)
+ return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+
+ if (!ModelState.IsValid)
+ return BadRequest(new ApiErrorResponse(ModelState));
+
+ //Instantiate the business object handler
+ DashboardViewBiz biz = DashboardViewBiz.GetBiz(ct, HttpContext);
+
+ var o = await biz.GetAsync();
+ if (o == null)
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+
+ if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
+ return StatusCode(403, new ApiNotAuthorizedResponse());
+
+ try
+ {
+ if (!await biz.PutAsync(o, inObj))
+ return BadRequest(new ApiErrorResponse(biz.Errors));
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ if (!await biz.ExistsAsync(id))
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+ else
+ return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
+ }
+ return Ok(ApiOkResponse.Response(new { Concurrency = o.Concurrency }));
+ }
+
+
+ //------------
+
+
+ }//eoc
+}//eons
\ No newline at end of file
diff --git a/server/AyaNova/biz/AyaType.cs b/server/AyaNova/biz/AyaType.cs
index bbea2da7..cb726e03 100644
--- a/server/AyaNova/biz/AyaType.cs
+++ b/server/AyaNova/biz/AyaType.cs
@@ -113,7 +113,8 @@ namespace AyaNova.Biz
CustomerServiceRequest = 54,
ServiceBank = 55,
OpsNotificationSettings = 56,
- Report = 57
+ Report = 57,
+ DashboardView=58
diff --git a/server/AyaNova/biz/DashboardViewBiz.cs b/server/AyaNova/biz/DashboardViewBiz.cs
new file mode 100644
index 00000000..7777e242
--- /dev/null
+++ b/server/AyaNova/biz/DashboardViewBiz.cs
@@ -0,0 +1,130 @@
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json.Linq;
+using AyaNova.Util;
+using AyaNova.Api.ControllerHelpers;
+using AyaNova.Models;
+
+
+
+namespace AyaNova.Biz
+{
+
+ //Only one dashboard view per user and there is always one so no delete method, not create method
+ internal class DashboardViewBiz : BizObject
+ {
+
+ internal DashboardViewBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
+ {
+ ct = dbcontext;
+ UserId = currentUserId;
+ UserTranslationId = userTranslationId;
+ CurrentUserRoles = UserRoles;
+ BizType = AyaType.DashboardView;
+ }
+
+ internal static DashboardViewBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
+ {
+ if (httpContext != null)
+ return new DashboardViewBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
+ else
+ return new DashboardViewBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //EXISTS
+ internal async Task ExistsAsync(long id)
+ {
+ return await ct.DashboardView.AnyAsync(z => z.Id == id);
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ /// GET
+
+ //Get one - will create if there isn't one to get
+ internal async Task GetAsync()
+ {
+ //This is simple so nothing more here, but often will be copying to a different output object or some other ops
+ var ret = await ct.DashboardView.SingleOrDefaultAsync(z => z.UserId == UserId);
+ if (ret == null)
+ {
+ ret = new DashboardView();
+ ret.UserId = UserId;
+ ct.DashboardView.Add(ret);
+ await ct.SaveChangesAsync();
+ }
+ return ret;
+ }
+
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //UPDATE
+ //
+
+ //put
+ internal async Task PutAsync(DashboardView dbObject, DashboardView inObj)
+ {
+ //always only current user id
+ inObj.UserId = UserId;
+
+ //Replace the db object with the PUT object
+ CopyObject.Copy(inObj, dbObject, "Id");
+ //Set "original" value of concurrency token to input token
+ //this will allow EF to check it out
+ ct.Entry(dbObject).OriginalValues["Concurrency"] = inObj.Concurrency;
+
+ Validate(dbObject, false);
+ if (HasErrors)
+ return false;
+ await ct.SaveChangesAsync();
+
+ return true;
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //VALIDATION
+ //
+
+ //Can save or update?
+ private void Validate(DashboardView inObj, bool isNew)
+ {
+ //Filter json must parse
+ //this is all automated normally so not going to do too much parsing here
+ //just ensure it's basically there
+ if (!string.IsNullOrWhiteSpace(inObj.View))
+ {
+ try
+ {
+ var v = JArray.Parse(inObj.View);
+
+ }
+ catch (Newtonsoft.Json.JsonReaderException ex)
+ {
+ AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "ListView", "ListView is not valid JSON string: " + ex.Message);
+
+ }
+ }
+
+ return;
+ }
+
+
+
+ /////////////////////////////////////////////////////////////////////
+
+ }//eoc
+
+
+}//eons
+