Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged development to master #39

Merged
merged 13 commits into from
Dec 30, 2017
Binary file added Database sample/TaskPlanner.mdf
Binary file not shown.
912 changes: 912 additions & 0 deletions Database sample/TaskPlannerDBSchema.htm

Large diffs are not rendered by default.

Binary file added Database sample/TaskPlanner_log.ldf
Binary file not shown.
Binary file added Database sample/Taskplanner.sql
Binary file not shown.
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,97 @@
# Task Planner
Task Planner - A simple easy to use application to create project roadmaps, user stories, project estimates effortlessly using easy to use interface. Created project can easily be shared to any one by adding the receipents email id.

# Basic Requirements to run
* VS2017 update 15.3 and above as it uses .net core 2.0
* MS SQL Server for hosting SQL Database (Sample DB can be found here and can be hosted for getting started) https://github.com/bharatdwarkani/taskplanner/tree/development/Database%20sample

# Changes need to be done in source to setup application
* Get a client and secret key by registering you app in google. For Login to work.
Reference - https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?tabs=aspnetcore2x
* Once registered replace client and secret id in apisettings.config file availabe in source https://github.com/bharatdwarkani/taskplanner/blob/development/TaskPlanner/appsettings.json
* Also replace sql server db credentials after hosting database in same file appsettings.json
* Now host application and it will be ready to run.

# Key Features

* Login using google account
* Share projects to any one by using there gmail id
* Create, edit, delete and share projects
* Effortlessly create road map ,user stories as if you are typing in an doc editor in a grid.
Categorize and group stories by theme, epics in a grid view
* Add estimates, penalty, benefit, priority, theme, epic, sprint, release, assignee and priority to a story.
* Export stories to excel
* Developed using cutting edge technology .net core - cross platform compatible can be hosted on linux / windows.
* Replaces email / excel and organizes features list at one place

* Note: This isn't an actual issue tracker but it helps in planning stories to be added in backlog of issue tracker its a first stage of your development cycle highly usefull for startup companies, consultancy team , product owners, scrum masters for writing user stories / feature plan effortlessly.

# Use Cases

* Start-up companies can use this to prepare road map for their products.
* Consulting companies can use this to prepare a plan or estimate cost / hour for requirements and can share to customer.
* Freelancers or small teams can use this to prepare a list of features for their product.
* Product Owners / Scrum master can use this to create a plan for their product release or sprints.
* Product Owners / Scrum master can use this to prepare and share road maps.
* As it provides organized for story writing effortlessly can be used as a replacement for document editors / excel sheets.
* There are several fields which can be used in different ways based on needs either for preparing road maps, sprint plan, release plan, preparing estimates.
* Stories can be exported to excel.
* Stories can be grouped / sorted by themes, epics, estimation, assignee, sprint / release easily.
* Estimate, penalty, benefit & priority can also be set for stories.
* Reduces time and effort to write stories by product owner as they are not writing stories in actual issue tracker, which takes time for a single-story creation, who can then pass on written stories to scrum masters to convert to actual tasks with detailed requirement in issue tracker.
* Can be used as an initial stage tool in software development life cycle for filtering features and setting priority.
* Can also be used as an application to create check lists.
* Share testing plan to testing engineers.



# Future Enhancements

* Import from Excel, CSV, Jira XML.
* Kanban boards for viewing stories by sprint, release, theme, epic
* Chart & Graph visualisation
* Count based Statistics
* Email to user when project is shared
* Retaing selected columns for a project in database so that state of selected columns are retained project wise.
* Integration with issue trackers JIRA, Trello, GitHub, GitLab.
* Improve Responsiveness to mobile
* Documents and screenshots maintenance

# Screenshots

Home page

![Homepage](http://www.syncfusion.com/downloads/support/directtrac/general/homepage-298396481.png "Homepage")

Google Authentication

![Login](http://www.syncfusion.com/downloads/support/directtrac/general/login1311576156.png "Login")

Projects - Add, Edit, Delete, Favourite

![Projects](http://www.syncfusion.com/downloads/support/directtrac/general/projects-461488668.png "Projects")

Sharing Projects

![Sharing Project](http://www.syncfusion.com/downloads/support/directtrac/general/share1386578163.png "Sharing Project")

Plan roadmaps / stories / features

![Planning Stories / Roadmap](http://www.syncfusion.com/downloads/support/directtrac/general/stories1753205535.png "Planning Stories / Roadmap")

Using additional columns other than those visble by default

![Selecting Columns](http://www.syncfusion.com/downloads/support/directtrac/general/columns1029361484.png "Selecting Columns")


# Third Party library credits

Thankfull to following third party library vendors aiding in implementation of features

* Bootstrap
* Toastr
* typescript
* Systemjs
* Syncfusion ej2 components (grid, button , dialog) - http://ej2.syncfusion.com


45 changes: 32 additions & 13 deletions TaskPlanner/Controllers/ProjectController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ public JsonResult AddProjectAsync(string description = "", string projectname =
if(id >0)
objects.ProjectId = id;
objects.CreatedBy = User.FindFirstValue(ClaimTypes.NameIdentifier);
var res = new ProjectViewModel().UpdateProjectDetails(objects);
var currentUserEmail = User.Identity.Name;

var res = new ProjectViewModel().UpdateProjectDetails(objects, currentUserEmail);
if (res.IsSuccess)
{
return this.Json(new
Expand Down Expand Up @@ -148,9 +150,13 @@ public JsonResult EditProjectAsync(string projectId = "")
/// Shareproject() - Return the partial view
/// </summary>
/// <returns>partial view</returns>
public PartialViewResult ShareProject()
public PartialViewResult ShareProject(int projectId)
{
var list = ProjectModel.GetProjectSharedList();
var currentUserEmail = User.Identity.Name.ToLower().Trim();



var list = ProjectModel.GetProjectSharedList(projectId, currentUserEmail);
//return this.PartialView("~/Views/Project/_ShareList.cshtml", list);
return this.PartialView("~/Views/Project/_ShareProject.cshtml", list);
}
Expand All @@ -163,13 +169,25 @@ public PartialViewResult ShareProject()
/// <returns></returns>
public JsonResult ShareEmail(int projectId, string email = "")
{
var res = new ProjectViewModel().UpdateProjectPermissionList(projectId, email,true);
var currentUserEmail = User.Identity.Name.ToLower().Trim() ;

email = email.ToLower().Trim();
if (email== currentUserEmail)
return this.Json(new
{
status = true,
message = "Owner already has permission for project."
});



var res = new ProjectViewModel().UpdateProjectPermissionList(projectId, email,true);
if (res.IsSuccess)
{
return this.Json(new
{
status = true,
message = "New project created successfully."
message = "Permission added successfully."
});
}
else
Expand All @@ -181,12 +199,7 @@ public JsonResult ShareEmail(int projectId, string email = "")
});
}
}

public ActionResult GetShareProjectList()
{
var list = ProjectModel.GetProjectSharedList();
return this.PartialView("~/Views/Project/_ShareList.cshtml", list);
}


/// <summary>
///
Expand All @@ -195,8 +208,14 @@ public ActionResult GetShareProjectList()
/// <returns></returns>
public JsonResult RemovePermission(int permissionId)
{
var res = new ProjectViewModel().RemoveProjectPermission(permissionId);
if (res.IsSuccess)

var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);

var res = new ProjectViewModel().RemoveProjectPermission(permissionId, userId);



if (res.IsSuccess)
{
return this.Json(new
{
Expand Down
34 changes: 21 additions & 13 deletions TaskPlanner/Models/ProjectModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,40 @@ public static ProjectObjects GetProjectList(string filter, string currentUserEma
{
projectList.ProjectListObjects = (from c in context.AspNetUsers.Where(y => (y.Email == currentUserEmail))
from d in context.ProjectPermissions.Where(i => i.IsActive && i.EmailId == currentUserEmail).DefaultIfEmpty()
from a in context.Projects.Where(x => (x.IsActive && (x.ProjectId == d.ProjectId || x.CreatedBy == c.Id)))
from a in context.Projects.Where(x => (x.IsActive && (x.ProjectId == d.ProjectId)))
from b in context.Favourites.Where(y => (y.UserId == c.Id && y.ProjectId == a.ProjectId && y.IsActive))
from owner in context.AspNetUsers.Where(y => (y.Id == a.CreatedBy)
)
select new ProjectListObjects
{
ProjectId = a.ProjectId,
ProjectName = a.ProjectName,
ProjectDescription = a.Description,
CreatedOn = a.CreatedOn,
CreatedBy = c.UserName,
Email = c.Email,
IsOwner = Permission.IsUserOwnerOfProject(currentUserEmail, a.ProjectId)
CreatedBy = owner.Email,
Email = owner.Email,
IsOwner = owner.Email==currentUserEmail
}).Distinct().ToList();
}

else if (filter == "all")
{
projectList.ProjectListObjects = (from b in context.AspNetUsers.Where(i => i.Email == currentUserEmail)
from c in context.ProjectPermissions.Where(z => (z.EmailId == currentUserEmail && z.IsActive)).DefaultIfEmpty()
from a in context.Projects.Where(x => x.IsActive && (x.ProjectId == c.ProjectId || x.CreatedBy == b.Id))
var projPermission = (from c in context.ProjectPermissions where c.EmailId == currentUserEmail && c.IsActive select c.ProjectId).ToList();

if(projPermission!=null && projPermission.Count>0)
projectList.ProjectListObjects = (
from a in context.Projects.Where(x => x.IsActive && projPermission.Contains(x.ProjectId ))
from b in context.AspNetUsers.Where(i => i.Id == a.CreatedBy)

select new ProjectListObjects
{
ProjectId = a.ProjectId,
ProjectName = a.ProjectName,
ProjectDescription = a.Description,
CreatedOn = a.CreatedOn,
CreatedBy = b.UserName,
CreatedBy = b.Email,
Email = b.Email,
IsOwner = Permission.IsUserOwnerOfProject(currentUserEmail, a.ProjectId)
IsOwner = b.Email == currentUserEmail
}).Distinct().ToList();
}
else
Expand All @@ -59,9 +65,9 @@ from a in context.Projects.Where(x => (x.IsActive && x.CreatedBy == b.Id))
ProjectName = a.ProjectName,
ProjectDescription = a.Description,
CreatedOn = a.CreatedOn,
CreatedBy = b.UserName,
CreatedBy = b.Email,
Email = b.Email,
IsOwner = Permission.IsUserOwnerOfProject(currentUserEmail, a.ProjectId)
IsOwner = true
}).Distinct().ToList();
}

Expand Down Expand Up @@ -121,12 +127,14 @@ from b in context.AspNetUsers.Where(y => y.Id == a.CreatedBy)
///
/// </summary>
/// <returns></returns>
public static ProjectShareObjects GetProjectSharedList()
public static ProjectShareObjects GetProjectSharedList(int projectId,string emailid)
{
var list = new ProjectShareObjects();
using (var context = new TaskPlannerEntities())
{
list.ProjectShareListObjects = (from a in context.ProjectPermissions.Where(x => (x.IsActive))
list.ProjectShareListObjects = (from a in context.ProjectPermissions.Where(x => (x.IsActive) && x.ProjectId== projectId
&& x.EmailId!=emailid
)
select new ProjectShareListObjects
{
PermissionId = a.PermissionId,
Expand Down
39 changes: 34 additions & 5 deletions TaskPlanner/Models/ProjectViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

public interface IProjectViewModel
{
TransactionResult UpdateProjectDetails(ProjectListObjects newProjectDetails);
TransactionResult UpdateProjectDetails(ProjectListObjects newProjectDetails,string email);
TransactionResult DeleteProject(int projectId);
TransactionResult UpdateFavouriteList(int projectId, string userId, bool isFavouriteListAdded);
TransactionResult UpdateProjectPermissionList(int projectId, string emailId, bool isProjectPermissionListAdded);
}

public class ProjectViewModel : IProjectViewModel
{
public TransactionResult UpdateProjectDetails(ProjectListObjects newProjectDetails)
public TransactionResult UpdateProjectDetails(ProjectListObjects newProjectDetails,string email)
{
var result = new TransactionResult();

Expand Down Expand Up @@ -48,8 +48,20 @@ public TransactionResult UpdateProjectDetails(ProjectListObjects newProjectDetai
IsActive = true
};
context.Projects.Add(newProject);

var projectPermissionListObj = new ProjectPermission()
{
ProjectId = newProject.ProjectId,
EmailId = email,
UpdatedOn = DateTime.Now,
IsActive = true
};
context.ProjectPermissions.Add(projectPermissionListObj);

}



context.SaveChanges();
}
result.IsSuccess = true;
Expand Down Expand Up @@ -148,7 +160,9 @@ public TransactionResult UpdateProjectPermissionList(int projectId, string email
using (var context = new TaskPlannerEntities())
{
var projectPermissionObj = (from projectPermissionDetails in context.ProjectPermissions.Where(i => i.ProjectId == projectId && i.EmailId == emailId)
select projectPermissionDetails).ToList();
select projectPermissionDetails).ToList();



if (isProjectPermissionListAdded)
{
Expand Down Expand Up @@ -197,7 +211,7 @@ public TransactionResult UpdateProjectPermissionList(int projectId, string email
/// </summary>
/// <param name="permissionId"></param>
/// <returns></returns>
public TransactionResult RemoveProjectPermission(int permissionId)
public TransactionResult RemoveProjectPermission(int permissionId,string userid)
{
var result = new TransactionResult();
try
Expand All @@ -207,7 +221,22 @@ public TransactionResult RemoveProjectPermission(int permissionId)
var projectPermissionObj = (from projectPermissionDetails in context.ProjectPermissions.Where(i => i.PermissionId == permissionId)
select projectPermissionDetails).FirstOrDefault();
projectPermissionObj.IsActive = false;
context.SaveChanges();

var favProjects = (from f in context.Favourites
where f.IsActive && projectPermissionObj.ProjectId == f.ProjectId && f.UserId == userid
select
f).ToList();

if (favProjects != null && favProjects.Count > 0)
{
foreach (var item in favProjects)
{
item.IsActive = false;
}
}


context.SaveChanges();
}

result.IsSuccess = true;
Expand Down
2 changes: 1 addition & 1 deletion TaskPlanner/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)

routes.MapRoute(
name: "Share project",
template: "project/shareproject",
template: "project/shareproject/{projectId?}",
defaults: new { controller = "Project", action = "ShareProject" });

routes.MapRoute(
Expand Down
1 change: 1 addition & 0 deletions TaskPlanner/TaskPlanner.csproj.user
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
<WebStackScaffolding_IsReferencingScriptLibrariesSelected>False</WebStackScaffolding_IsReferencingScriptLibrariesSelected>
<WebStackScaffolding_IsAsyncSelected>False</WebStackScaffolding_IsAsyncSelected>
<WebStackScaffolding_ViewDialogWidth>600</WebStackScaffolding_ViewDialogWidth>
<NameOfLastUsedPublishProfile>CustomProfile</NameOfLastUsedPublishProfile>
</PropertyGroup>
</Project>
8 changes: 5 additions & 3 deletions TaskPlanner/Views/Project/_ShareProject.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
</div>
</form>

<div>
@*<div>
<div class="e-float-input" style="margin-top: 0px">
<input type="text" id="shareLinks">
</div>
</div>
</div>*@

<style>
tr {
Expand Down Expand Up @@ -89,6 +89,8 @@

@if (Model != null && Model.ProjectShareListObjects != null && Model.ProjectShareListObjects.Count > 0)
{
<div style="font-weight:400!important;color:rgba(0,0,0,0.87)!important ">Users who has access to this project</div>

<table style="width: 100%; margin-top:20px;">
<tbody>
@foreach (var detail in Model.ProjectShareListObjects)
Expand All @@ -110,5 +112,5 @@
}
else
{
<span style="margin-left: 7px;">No results found</span>
<span style="margin-left: 5px;margin-top:10px">No user other than you have access to this project. To share this project add gmail id of user above. Once added user can access this project</span>
}
Loading