#Standards ##Colors
- Inactive border: border-gray-200
- Active border: border-indigo-400 text-white
- Active element: bg-indigo-400 text-white
- Hover background: bg-gold-100
- Text: text-gray-800
- Primary color: indigo-500
- Secondary color: gray-500
- Danger color: red-500
- Info background color: blue-100
- Info icon color: blue-500
- Warning color: yellow-500
- Success color: green-100
- OK color: green-500
#To do (v2020.2) ###Authn
- Allow user to request another activiation link
- Allow user to set for which events an email should be sent.
- Remove an account completely with an option to download all data and files for all projects etc. that would have been stored in the any of the owned workspaces.
- Complement User tests with the Settings stuff
- Delete settings when User model is deleted ###Clients ###Core
- Intercept 412 responses and fetch the desired object first rejecting the original alteration request with a generic message: "Object was externally modified, so your changes could not be applied. The modifications were loaded from the API, and should be visible."
- Support setting moment locale based on Laravel / browser locale. ###i12 ###Projects
- Write tests around ProjectRepository
- Delete Project in SPA
- Archive Project in SPA
- Update Project in SPA
- Write tests around Project API
- Close project. This stops timesheet entries being created against those projects (even historic timesheet entries cannot be started against closed projects). ###Public-Web ###Tasks ###Timesheets
- Make timesheet report graphs responsive (including ticks on X axis) ###UI-UX
- Make
PmtechInput
a global component - Add FlashMessage component of some variety to show info, warning, success and global error messages.
- Update window title with
title
from routes in the afterEach guard ###Workspaces - Add full member profile in order to understand email domain and country information for each user / subscriber. This can be used to determine the amount of users in a workspace and to determine whether or not VAT is to be applied to the billable amount.
- Make sure WorkspaceUpdated events are send (via Pusher) to web clients listening on the Workspace private channel. Make sure to have this covered in test cases.
- Complete WorkspacePolicy with reference to all Business Requirements
- Write WorkspaceController actions
- Create routes for workspace things - CRUD actions for API
#To do (v2020.3) ###Clients ###Core ###General ###Navigation ###Projects
- Add
price
attribute for projects - Add
reduction_percentage
to Project - When adding price to project, show estimate based on project tasks and profit_margin.
- Allow project member to overwrite
cost
from workspace member - Allow project to overwrite
profit_margin
from workspace ###Tasks - Add
percent_completion
toTask
- Add Completion field to GanttChart's GridTable
- Add Completion field to TaskProperties (if all tasks are at 100%, show a checked checkbox, otherwise show a number input min 0 max 100). If not all tasks have the same percent_completion value, do not allow editing of this field.
- Add
nesting_level
andparent_task_id
to Task model - Add actual
duration
,ended_at
,started_at
andwork
to Task model - Add planned
duration
,ended_at
,started_at
andwork
to Task model - ForceDelete Task in SPA only if task has no timesheet entries
- Reorder tasks (via buttons in toolbar)
- Send TaskUpdated event when task was updated
- When task is work-driven, update % complete according to approved timesheet records
- Mark task as 100% or completed. This stops timesheet entries being created against those tasks (even historic timesheet entries cannot be started against completed tasks). ###Timesheets ###UI/UX ###Users ###Workspace
- Add
cost
per workspace member - Add
profit_margin
to workspace
#To do (v2021.1)
#To do (unassigned to release)
- When user accepted invitation to join or was added to a workspace, the workspace owner should be notified that the user now needs access to projects in order to collaborate with other project members.
- Bruteforce POST login protection - Make sure that users cannot fail login attempts more than 5 times in 5 seconds
- Add Brute Force protection around registration route as well.
- Submit weekly timesheet
- Lock timesheet entries after submitting
- Add hourly cost to workspace or project members
- Add type to project - hourly, fixed, retainer
- Currency per workspace and apply xe exchange rate between project and workspace defined in the workspace yet
- Unit test NotificationRepository
- Write Notification routes and Controller methods
- Unit test Notification API
- Create Notification controller, migration and model
- Write NotificationRepository
- Project Dashboard to list all tasks' main details (name) and total hours recorded against each task
- Add Currency model / migration
- Link
Workspace
andProject
toCurrency
- Automatically convert to workspace currency any project that has financial details in a different currency (payable and submitted invoices). Use the package mentioned in Laravel News "Laravel Exchange Rates is a package by Ash Allen for interacting with the exchangeratesapi.io API.".
- Implement logic in Login SPA Component to go to the back URL
- Listen to private broadcast channel for each project in Vuex
- GanttChart should only update API if the task was truly altered
- Global "loading" div with incrementLoading and decrementLoading vuex committers
- Replace App.vue
loading
with a full modal div that shows whenever there are Axios requests in progress for longer than 2 seconds GridTable
to supportsortable
flag in field definition and sorting by multiplesortable
fieldsGridTable
to support notion of visible and hidden columns. Right click shows a popup menu where one can select to make hidden columns visible; also one can make visible columns hidden- BR000003 - Add project to workspace should send Notification to Workspace Owner
- BR000004 - Even though tasks can be archived or deleted, this cannot happen when there are timesheet entries against a task.
- BR000005 - Even though projects can be archived or deleted, this cannot happen when there are timesheet entries against a project.
- BR000010 - Users with the role "Line Manager" or "Project Manager" can approve submitted timesheets
- BR000011 - Approved timesheet entries can only be archived by users with the role "Programme Manager" or "Portfolio Manager"
- BR000012 - Lock timesheet entries after submitting so that they cannot be edited by the user, but only approved / rejected by the respective Manager users
- BR000014 - Write cypress tests to make sure that the correct confirmation messages are shown.
#To do (Good first issue)
#In Progress
- Rewrite repositories to implement a common interface and derive from a common parent class
- v2020.2 - Core -
#Done
- BR000001 - Setup a "Default" workspace when a user registers
- BR000002 - Allow AccountActivation email to be regenerated upon user request
- BR000006 - Any project_user can create a timesheet entry against any task in the project or against the project it self
- BR000007 - Any workspace_user can create a timesheet entry against that workspace.
- BR000008 - TimesheetEntries can be created against a project as such or a task or simply in the workspace
- BR000009 - Users can edit only their own timesheet entries
- BR000013 - Workspace names should be unique for the ownerUser
- BR000014 - If a user deletes / archives his / her last owned workspace, warn the user specifically that that is the last owned workspace. The user can always create a new workspace any time (s)he wants.
- BR000015 - Client name must unique inside a workspace
- BR000016 -
TimesheetEntry
.started_at
must be beforeTimesheetEntry
.ended_at
- BR000017 - Two timesheet entries for the same user (regardless of workspace / project / task) may not overlap.
- BR000018 - When POSTing a new timesheet entry, any still running
timesheet entries must be stopped by setting their
ended_at
attribute to one second before the new entry'sstarted_at
attribute. - BR000019 - Timesheet entries can only be created through the API when a user is logged in.
- BR000020 - A user must have at least 1 owned Workspace at all times, unless the user is deleting its account.
- BR000021 - Only workspace users can retrieve workspace details from the API
- BR000022 - Project names must be unique within a workspace
- Create workspaces table - id (uuid), owner_user_id, name
- Create Workspace model
- Create relationship between User and Workspace models
- Create WorkspaceRepository - CRUD actions
- Write tests around the WorkspaceRepository
- Create TE Seeder
- Hide id, user_id, in Settings
- User model $with settings
- Create a user Settings Model and table (id (int), user_id, last_visited_view)
- Create settings record when a user is created.
- Create SettingsRepository
- Create command to create user settings if the user doesn't have it yet
- Login via the SPA - Login screen, logic, and redirect
- Make sure browser refresh (F5) works with Laravel routing into SPA
- Allow user to logout from SPA
- Allow
last_visited_view
update in Settings through SettingsController - Add Project model / controller / migration
- projects table has workspace_id, color, name
- Implement relationships in Project model
- Write ProjectRepository
- Project API list projects for workspace
- Project API add project to workspace
- Load projects from API when changing into new workspaces router view
- WorkspaceDashboard should display a call to action if there are no projects
- Create new Project in SPA
- Add "color" and "id" to the visible properties of the Project model
- Workspace Dashboard should list each project's main details and show an "Add project" button
- Send WorkspaceUpdated notification via Pusher when project is added
- Add vue-router links for projects (nested under workspaces)
- Add Task model / controller / migration
- Write TaskRepository
- Write tests around TaskRepository
- Allow task index to be paginated
- Create new Task in SPA
- Order tasks in the index by their
wbs
by default - Add TimesheetEntry model / controller / migration
- Write TimesheetEntryRepository
- Write tests around TimesheetEntryRepository
- Write TimesheetEntry API and corresponding tests
- Create timesheet entry editor form
- Write Authorization policy around timesheet entries
- Display 5 days worth of timesheet entries in the TimesheetEditor
- Create user_workspace and project_user
- Implement Project Selector for Timesheet Entry Editor
- The TimesheetEntryEditor should display the correct project details
- Implement Task Selector for Timesheet Entry Editor
- Show duration for Timesheet Entry Editors that are not running a new entry
- Start new timesheet entry from history
- Allow Timesheet Index By Day to be collapsed / expanded
- Style Timesheet Entry Editor
- Style Timesheet Index By Day
- Update API as and when "description" is updated in
TimesheetEntryEditor
with a delay of 2 seconds. - Updates in any input in the Timesheet Entry Editor should result in a PUT request to update the API
- Style navigation horizontall on the top , but make it responsive
- Add dropdown menu "Timesheets" into menu bar (see https://tailwindcss.com/course/making-the-dropdown-interactive/)
FilteringDropdownControl
should highlight (bg-indigo-400
) the selected entry and scroll it into view, whilst keeping the down and up keys working- Disable
TimesheetEntryStartButton
when the description is empty TimesheetEntryEditor
needs to change betweenTimesheetEntryStartButton
and (new)TimesheetManualSaveButton
components- Implement Date/Time picker
- Use
DateTimePicker
inTimesheetManualSaveButton
- Use
DateTimePicker
in theTimesheetEntryStartButton
to display historicalstarted_at
time - Use
DateTimePicker
in theTimesheetEntryEditor
for setting a newstarted_at
time - Style the running timesheet entry a bit better and include a
DateTimePicker
to reset the start time. - Instead using a global onBlur event, use backdrops to capture "outside" click
events for any element that has a popup (see https://tailwindcss.com/course/making-the-dropdown-interactive/
at about 04:40). See also
TimesheetDropdownNavItem
. - Migrate to Laravel Airlock
- When a user creates a new project, that user should be associated with that project in project_user
- Add Logout to person's dropdown menu on the far right...
- When logging out, the authenticated Vuex state needs to change
- Generate timesheet report per workspace. Drill down per user, project or task
- When clicking the start button, after selecting project & task, the selection for project and task disappears
- When adding a project, the vuex store needs to reload its projects (through a Workspace Update Notification via Pusher?)
- Export timesheet report
- Style input boxes according to https://codesandbox.io/s/vue-template-lldw2?from-embed
- Implement a generic error handler (also in the API)
- Style and implement front-end
- Style back-end
- Make sure Laravel Airlock can authorize broadcasting private channel access
- Let the user create a new workspace in SPA
- Let the user switch between workspaces
- Update Task in SPA
- Look and feel of activation email - Just verify it, modify it if needed - Depends on the look and feel of the main site... Currently left blank without styles, really.
- Start writing e2e UI tests using cypress.io
- Migrate data from current www.project-management.tech to new www.project-management.tech implementation
- Let the user archive a workspace in SPA
- Let the user delete a workspace in SPA
- Let the user edit the workspace name in SPA
- Invite users (new and existing) to the workspace
- Let the user assign ownership of a workspace to another user
- When the workspace has more than 5 members the owner needs to pay
- When an email address registers again, but was not verified, send the whole verification email again (also create a cache entry).
- When an email address registers again, and it was verified before, the request should send an Unauthorized. response.
- Add Client model / migration / controller / repository
- Allow user to add client in SPA
- Projects can be associated with a client
- Add project allows user to pick a client
- Timesheet report can be drilled down by Client
- Support i18n in terms of month and day names
- Support Clicking Year to show a list of 10 decades (based on the current Year). Each decade can be clicked to pick a specific year in that decade
- Support Clicking Month to show a list of months that can be picked
- Support Clicking Hour to show a list of all hours that can be picked
- Support Clicking Minute / Second to show a list of 12 minutes / seconds (5 minutes / seconds between each, i.e. 0 5 10 15 etc) that can be picker
- Support proper config merging (
defaultConfig
indata
,passedConfig
inprops
and a new, merged,config
indata
which is passed on to subcomponents) - Rename
FilteringDropdownControl
toComboControl
- Support native hover events to recalculate
highlightedEntryIdx
andhighlightedEntryId
based on those hover events - Receive a lot of 419 errors after 1 hour of not interacting with the site. Implement silent keep-alive or increase lifetime of session / Airlock cookie.
- Allow user to switch workspaces in xs / sm screens
- Add
abbreviation
andstart_date
attributes to project Model - Show a small
TimesheetEntryEditor
in the bottom right hand corner when the user has a timesheet entry started and is not visiting the timesheet editor. - Make sure the ComboControl filtering matches case-insensitively a regex, rather than just the start
- Remove members from workspace
- Create simple analytics where we store only the user agent and screen size information in the database.
- Implement SettingsRepository and use it in SettingsController
- Write tests around SettingsRepository and SettingsController
- UserRepository should not create Settings object. That has to be done in a UserObserver class.
- Add dropdown menu "Projects" into menu bar (see https://tailwindcss.com/course/making-the-dropdown-interactive/)
- Order projects in the index by their
name
by default - Allow project index to be ordered by name, progress, etc.
- Add test that Settings are created when account was verified
- Add E-Tag support
- In middleware to verify against put requests
- In middleware to add to the response headers
- In models being updated out of the Vuex Store
- Complete CRUD actions in UserRepository - And write corresponding test cases
GridTable
to support clicking on row headers to select entire row. Multiple selections are possible through use of Shift-Click and Ctrl-Click- Add toolbar with task and project related buttons just above the Gantt Chart
- Allow task to be work-driven
- GridTextEditor needs to send input updates more frequently than onBlur
GridTable
needsGridPercentageEditor
- Protect tasks with e-tags (so that multiple clients can view tasks...)
- Listen to private broadcast channel for each task in Vuex
WorkspaceRepository::get
should be allowed to return NULL- Project and Workspace
id
cannot be updated through their respective repositories - v2020.2
- Core
- Upgrade to Laravel 7.
- Repository
findBy
methods need to distinguish between find first and find all.
- Core
#Details
##BR000001
The workspace should only be created when the user verifies the email address.
##Login via the SPA - Login screen, logic, and redirect
Correct login works and we can redirect already. Needs tweaking to correct URL
(/members/workspaces/:id), so we need to verify the user profile settings for
the last visited Vue view, and if none, we need to check the list of workspaces
to go to the one workspace, if not -> we simply go to the first workspace
in the list.
##Send WorkspaceUpdated notification via Pusher
Only send the id of the affected Workspace. Load the workspace from the API. If
it is the current workspace, also dispatch "workspaceChanged".
##ETag support
Supporting ETags on API endpoints is fairly straightforward: grab the response
from the request pipeline, and calculate some kind of strong hash (eg. sha1
).
Compare that value against the value of the request header If-None-Match and
send appropriate back to the API caller. This is what Laravel's built-in
Cache-Control (via cache.headers
middleware) supports.
However, if one wants to store a number of Laravel Models in a Vuex store for easy access and retrieval without the need to keep going back to the API to retrieve these models a number of times, we need some mechanism to retrieve an ETag for each individual model in that index.
There's two ways to go about it:
- Only send IDs as index response, and let the SPA fire off a number of GET requests as necessary to retrieve each Model's ETag.
- Calculate ETag hash values on individual objects.
Option 1. has the potential to violate Laravel's throttle middleware, and thus each of those subsequent GET requests would need to be fired off at a rate of one request per second. This is not practicle in large-ish applications.
Option 2. can be implemented in various ways. I've looked at:
- jsonSerialization overload
- Response Facade extension
- API Middleware
Option 1. would work in terms of generating the ETag for each individual object, it would be called any time the API wants a serialized view of the model. This would open a potentially catastrophic amount of hash calculation that may slow the API right down. Also, it is not entirely clear how an index API call would provide back all the individual ETags.
Option 2. would work as well, and is, in fact, proposed by @fideloper, see https://fideloper.com/laravel4-etag-conditional-get.
Option 3. is a direct result of that article because I started looking into the
$response object as a whole, and saw, much to my surprise, that we have a member
original
in the Response instance that we can get to by calling
$response->getOriginalContent()
. For GET requests, then, where the response
has an Eloquent Collection, we can calculate ETags for each Model in that
collection, just before the response is sent to the API consumer.
We can put the Model's ID in combination with the ETag in the response headers,
and we can list multiple combinations like that by using the semicolon to
separate entities. All we have to do now, when we commit an index response to
Vuex, is parse the ETag header, and store the ETag with the individual object in
Vuex. Thus we can use that ETag value next time the SPA uses the DELETE, GET or
PUT method on the given model. We can then also implement a response interceptor
that verifies for 304 responses or for 412 in case of mid-air collisions.