From b772b59fe64b76c9300ed6d4ea5eddb3dcde8a10 Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Wed, 16 Mar 2022 12:17:03 +0530 Subject: [PATCH 01/21] Refactor projects page to use react component Refactored to use component but page contents are not added yet --- app/controllers/projects_controller.rb | 3 +- .../src/components/Projects/index.tsx | 0 app/policies/project_policy.rb | 4 + app/views/projects/index.html.erb | 95 ++----------------- 4 files changed, 15 insertions(+), 87 deletions(-) create mode 100644 app/javascript/src/components/Projects/index.tsx diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8ad6b7b8b5..191738cd64 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,8 +4,7 @@ class ProjectsController < ApplicationController skip_after_action :verify_authorized def index - @query = Project.ransack(params[:q]) - @projects = @query.result(distinct: true) + authorize :project end def create diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 1fc36b074d..33dac524e4 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class ProjectPolicy < ApplicationPolicy + def index? + user_owner_or_admin? + end + def create? user_owner_or_admin? end diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index 3d10602124..b0f51212bf 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -1,91 +1,16 @@ -
-
-
-
- -
-
-

- <%= t('projects.projects') %> -

-
-
-
- <%= search_form_for @query do |f| %> - <%= f.search_field :name_cont, class: 'rounded tracking-wider appearance-none border border-gray-100 block w-72 sm:w-96 px-3 py-2 bg-miru-gray-100 h-8 shadow-sm font-semibold text-xs text-miru-dark-purple-1000 focus:outline-none focus:ring-miru-gray-1000 focus:border-miru-gray-1000 sm:text-sm', placeholder: 'Search' %> - <%= image_submit_tag 'search_icon.svg', class: "h-3 text-miru-gray-400 absolute inset-y-2 right-0 pr-3 flex items-center cursor-pointer" %> - <% end %> -
-
- - <% if policy(Project).create? %> -
- -
- <% end %> +
+
+
+
+
+

+ <%= t("projects.projects") %> +

- - -
-
-
-
- - - - - - - - - - - <% @projects.each do |project| %> - - - - - - <% end %> - - -
- <%= t('projects.project').upcase %>/<%= t('projects.client').upcase %> - - <%= t('project.hours_logged') %> - - - - <%= t('projects.edit') %> -
- <%= project.name.capitalize %> - - <%= project.timesheet_entries.collect(&:duration).first %> - -
-
-
-
-
-
-
-
-
-
-
-
<%= t('projects.add_new_project').capitalize %>
- -
- <%= render "projects/new"%> -
-
+ <%= react_component("Projects") %> +
- - From 2a3468287f0b24770776f4b95124015a7e8cc99b Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Wed, 16 Mar 2022 13:28:32 +0530 Subject: [PATCH 02/21] Add internal api to get projects list --- .../internal_api/v1/projects_controller.rb | 14 ++++++++++++++ app/javascript/src/apis/projects.ts | 9 +++++++++ app/models/project.rb | 19 +++++++++++++++++++ config/routes/internal_api.rb | 2 +- 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 app/controllers/internal_api/v1/projects_controller.rb create mode 100644 app/javascript/src/apis/projects.ts diff --git a/app/controllers/internal_api/v1/projects_controller.rb b/app/controllers/internal_api/v1/projects_controller.rb new file mode 100644 index 0000000000..50956ad87a --- /dev/null +++ b/app/controllers/internal_api/v1/projects_controller.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class InternalApi::V1::ProjectsController < InternalApi::V1::ApplicationController + def index + authorize :project + + render json: { projects: projects }, status: :ok + end + + def projects + projects = current_user.current_workspace.projects + projects.kept.map { | project | { id: project.id, name: project.name, minutes_spent: project.total_hours_logged } } + end +end diff --git a/app/javascript/src/apis/projects.ts b/app/javascript/src/apis/projects.ts new file mode 100644 index 0000000000..1df5024481 --- /dev/null +++ b/app/javascript/src/apis/projects.ts @@ -0,0 +1,9 @@ +import axios from "axios"; + +const path = "/projects"; + +const get = async () => axios.get(`${path}`); + +const projects = { get }; + +export default projects; diff --git a/app/models/project.rb b/app/models/project.rb index 3e94ec7609..ee38ef8bee 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -41,6 +41,25 @@ def project_member_full_names end end + def total_hours_logged(time_frame = "week") + from, to = week_month_year(time_frame) + timesheet_entries.where(work_date: from..to).sum(:duration) + end + + # Move weeK_month_year method copied from client.rb to common place + def week_month_year(time_frame) + case time_frame + when "last_week" + return ((Date.today.beginning_of_week) - 7), ((Date.today.end_of_week) - 7) + when "month" + return Date.today.beginning_of_month, Date.today.end_of_month + when "year" + return Date.today.beginning_of_year, Date.today.end_of_year + else + return Date.today.beginning_of_week, Date.today.end_of_week + end + end + private def discard_project_members project_members.discard_all diff --git a/config/routes/internal_api.rb b/config/routes/internal_api.rb index 3bc1787a18..41da763bc8 100644 --- a/config/routes/internal_api.rb +++ b/config/routes/internal_api.rb @@ -3,7 +3,7 @@ namespace :internal_api, defaults: { format: "json" } do namespace :v1 do resources :clients, only: [:index, :update, :destroy, :show] - resources :project, only: [:index] + resources :projects, only: [:index] resources :timesheet_entry, only: [:index, :create, :update, :destroy] resources :reports, only: [:index] resources :workspaces, only: [:update] From 010099afc969884a79b37db539d99be194fe7c92 Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Wed, 16 Mar 2022 13:33:22 +0530 Subject: [PATCH 03/21] Add projects index page component file --- .../src/components/Projects/index.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index e69de29bb2..fd9070e015 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; +import { setAuthHeaders, registerIntercepts } from "apis/axios"; +import projects from "apis/projects"; + +const Projects = () => { + const [allProjects, setAllProjects] = React.useState([]); + + const fetchProjects = async () => { + const res = await projects.get(); + if (res.status == 200) { + setAllProjects(res.data.projects); + } + }; + + React.useEffect(() => { + setAuthHeaders(); + registerIntercepts(); + fetchProjects(); + }, []); + + return ( +
+ {allProjects.map(p =>
{p.id}
)} +
+ ); + +}; + +export default Projects; From 3d8e7003b6d41b203e2d4b1c196a31fe3f7318db Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Wed, 16 Mar 2022 18:02:47 +0530 Subject: [PATCH 04/21] Add project details to project's index page --- .../internal_api/v1/projects_controller.rb | 7 +- .../src/components/Projects/index.tsx | 71 +++++++++++++++- .../src/components/Projects/project.tsx | 81 +++++++++++++++++++ app/views/projects/index.html.erb | 5 +- 4 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 app/javascript/src/components/Projects/project.tsx diff --git a/app/controllers/internal_api/v1/projects_controller.rb b/app/controllers/internal_api/v1/projects_controller.rb index 50956ad87a..23e2311070 100644 --- a/app/controllers/internal_api/v1/projects_controller.rb +++ b/app/controllers/internal_api/v1/projects_controller.rb @@ -9,6 +9,11 @@ def index def projects projects = current_user.current_workspace.projects - projects.kept.map { | project | { id: project.id, name: project.name, minutes_spent: project.total_hours_logged } } + projects.kept.map { | project | + { id: project.id, + name: project.name, + clientName: project.client.name, + isBillable: project.billable, + minutesSpent: project.total_hours_logged } } end end diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index fd9070e015..9a3fb00249 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -1,8 +1,10 @@ import * as React from "react"; +import { ToastContainer } from "react-toastify"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projects from "apis/projects"; +import { Project } from "./project"; -const Projects = () => { +const Projects = (isAdminUser) => { const [allProjects, setAllProjects] = React.useState([]); const fetchProjects = async () => { @@ -19,9 +21,70 @@ const Projects = () => { }, []); return ( -
- {allProjects.map(p =>
{p.id}
)} -
+ <> + +
+
+
+
+ + + + + + + + + + + + {allProjects.map((project, index) => ( + + ))} + +
+ PROJECT/CLIENT + + + + HOURS LOGGED +
+
+
+
+
+ {/* {showEditDialog ? ( + + ) : null} + {showDeleteDialog && ( + + )} */} + ); }; diff --git a/app/javascript/src/components/Projects/project.tsx b/app/javascript/src/components/Projects/project.tsx new file mode 100644 index 0000000000..87b53ec65e --- /dev/null +++ b/app/javascript/src/components/Projects/project.tsx @@ -0,0 +1,81 @@ +import * as React from "react"; +import { minutesToHHMM } from "../../helpers/hhmm-parser"; + +export interface IProject { + id: number; + name: string; + clientName: string; + isBillable: boolean; + minutesSpent: number; + editIcon: string; + deleteIcon: string; + isAdminUser: boolean; + setShowEditDialog: any; + setProjectToEdit: any; + setProjectToDelete: any; + setShowDeleteDialog: any; +} + +export const Project = ({ + id, + name, + clientName, + minutesSpent, + isBillable, + editIcon, + deleteIcon, + isAdminUser, + setShowEditDialog, + setProjectToEdit, + setProjectToDelete, + setShowDeleteDialog +}: IProject) => { + const [grayColor, setGrayColor] = React.useState(""); + const [isHover, setHover] = React.useState(false); + + const handleMouseEnter = () => { + setGrayColor("bg-miru-gray-100"); + setHover(true); + }; + + const handleMouseLeave = () => { + setGrayColor(""); + setHover(false); + }; + + return ( + + + {name}" "{clientName} + + + {isBillable} + + + {minutesToHHMM(minutesSpent)} + + + {isAdminUser && isHover && + } + + + { isAdminUser && isHover && + } + + + ); +}; diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index b0f51212bf..f4bcd9ed18 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -9,7 +9,10 @@
- <%= react_component("Projects") %> + <%= react_component("Projects", { + isAdminUser: current_user.has_owner_or_admin_role?(current_user.current_workspace) + }) %> +
From 04308e0ecc650ec2868f16570a53d1f284d138ec Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Thu, 17 Mar 2022 12:54:31 +0530 Subject: [PATCH 05/21] Add component for project list Also added state to check if project is clicked for showing project details --- .../src/components/Projects/index.tsx | 81 ++++--------------- .../src/components/Projects/projectList.tsx | 73 +++++++++++++++++ 2 files changed, 90 insertions(+), 64 deletions(-) create mode 100644 app/javascript/src/components/Projects/projectList.tsx diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 9a3fb00249..724ef4c08b 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -3,9 +3,11 @@ import { ToastContainer } from "react-toastify"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projects from "apis/projects"; import { Project } from "./project"; +import ProjectList from "./projectList"; const Projects = (isAdminUser) => { const [allProjects, setAllProjects] = React.useState([]); + const [showProjectDetails, setShowProjectDetails] = React.useState(); const fetchProjects = async () => { const res = await projects.get(); @@ -20,71 +22,22 @@ const Projects = (isAdminUser) => { fetchProjects(); }, []); - return ( - <> - -
-
-
-
- - - - - - - - - - - - {allProjects.map((project, index) => ( - - ))} - -
- PROJECT/CLIENT - + const projectClickHandler = (pid) => { + setShowProjectDetails(pid); + }; - - HOURS LOGGED -
-
-
-
-
- {/* {showEditDialog ? ( - - ) : null} - {showDeleteDialog && ( - - )} */} - + const renderProjectDetails = () => ( + <> + //{} + ); + + return ( + showProjectDetails ? + renderProjectDetails() : + ); }; diff --git a/app/javascript/src/components/Projects/projectList.tsx b/app/javascript/src/components/Projects/projectList.tsx new file mode 100644 index 0000000000..95afbcc982 --- /dev/null +++ b/app/javascript/src/components/Projects/projectList.tsx @@ -0,0 +1,73 @@ +import * as React from "react"; +import { ToastContainer } from "react-toastify"; +import { Project } from "./project"; + +export const ProjectList = ({ allProjects, isAdminUser, projectClickHandler }) => ( + <> + +
+
+
+
+ + + + + + + + + + + + {allProjects.map((project, index) => ( + + ))} + +
+ PROJECT/CLIENT + + + + HOURS LOGGED +
+
+
+
+
+ {/* {showEditDialog ? ( + + ) : null} + {showDeleteDialog && ( + + )} */} + +); + +export default ProjectList; From 1df957eb90eac963a9094a173a0098353a6c4947 Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Thu, 17 Mar 2022 18:03:04 +0530 Subject: [PATCH 06/21] Show project details page when project gets clicked --- app/javascript/src/components/Projects/index.tsx | 15 ++++++--------- .../src/components/Projects/project.tsx | 8 +++++++- .../src/components/Projects/projectDetails.tsx | 7 +++++++ .../src/components/Projects/projectList.tsx | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 app/javascript/src/components/Projects/projectDetails.tsx diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 724ef4c08b..ef793b9dd1 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -3,11 +3,12 @@ import { ToastContainer } from "react-toastify"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projects from "apis/projects"; import { Project } from "./project"; +import ProjectDetails from "./projectDetails"; import ProjectList from "./projectList"; const Projects = (isAdminUser) => { const [allProjects, setAllProjects] = React.useState([]); - const [showProjectDetails, setShowProjectDetails] = React.useState(); + const [showProjectDetails, setShowProjectDetails] = React.useState(null); const fetchProjects = async () => { const res = await projects.get(); @@ -22,18 +23,14 @@ const Projects = (isAdminUser) => { fetchProjects(); }, []); - const projectClickHandler = (pid) => { - setShowProjectDetails(pid); + const projectClickHandler = (id) => { + setShowProjectDetails(id); }; - const renderProjectDetails = () => ( - <> - //{} - ); - return ( showProjectDetails ? - renderProjectDetails() : + : + projectClickHandler(id)}> {name}" "{clientName} diff --git a/app/javascript/src/components/Projects/projectDetails.tsx b/app/javascript/src/components/Projects/projectDetails.tsx new file mode 100644 index 0000000000..731fc62032 --- /dev/null +++ b/app/javascript/src/components/Projects/projectDetails.tsx @@ -0,0 +1,7 @@ +import * as React from "react"; + +const ProjectDetails = ({ id }) => ( + <>Showing details for project {id} +); + +export default ProjectDetails; diff --git a/app/javascript/src/components/Projects/projectList.tsx b/app/javascript/src/components/Projects/projectList.tsx index 95afbcc982..9b38528954 100644 --- a/app/javascript/src/components/Projects/projectList.tsx +++ b/app/javascript/src/components/Projects/projectList.tsx @@ -40,7 +40,7 @@ export const ProjectList = ({ allProjects, isAdminUser, projectClickHandler }) = key={index} {...project} isAdminUser={isAdminUser} - onClick={projectClickHandler} + projectClickHandler={projectClickHandler} /* editIcon={editIcon} deleteIcon={deleteIcon} setShowEditDialog={setShowEditDialog} From 9aa24b8ac70e930120786afae44f734eba0de00b Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Mon, 21 Mar 2022 12:41:27 +0530 Subject: [PATCH 07/21] Add API to fetch project details --- .../internal_api/v1/projects_controller.rb | 40 ++++++++++++++----- app/javascript/src/apis/projects.ts | 4 +- .../src/components/Projects/index.tsx | 11 ++--- app/models/project.rb | 9 +++++ app/policies/project_policy.rb | 4 ++ config/routes/internal_api.rb | 2 +- 6 files changed, 54 insertions(+), 16 deletions(-) diff --git a/app/controllers/internal_api/v1/projects_controller.rb b/app/controllers/internal_api/v1/projects_controller.rb index 23e2311070..dce7a3b461 100644 --- a/app/controllers/internal_api/v1/projects_controller.rb +++ b/app/controllers/internal_api/v1/projects_controller.rb @@ -2,18 +2,40 @@ class InternalApi::V1::ProjectsController < InternalApi::V1::ApplicationController def index - authorize :project + authorize Project render json: { projects: projects }, status: :ok end - def projects - projects = current_user.current_workspace.projects - projects.kept.map { | project | - { id: project.id, - name: project.name, - clientName: project.client.name, - isBillable: project.billable, - minutesSpent: project.total_hours_logged } } + def show + authorize Project + + render json: { project: project }, status: :ok end + + private + def projects + projects = current_user.current_workspace.projects + projects.kept.map { | project | + { id: project.id, + name: project.name, + clientName: project.client.name, + isBillable: project.billable, + minutesSpent: project.total_hours_logged } } + end + + def project + project = Project.find(params[:id]) + project_members = project.project_member_details.map { | member | + { name: member[:name], + hourlyRate: member[:hourly_rate], + minutesSpent: member[:minutes_spent], + cost: member[:hourly_rate] * member[:minutes_spent] / 60 }} + { id: project.id, + name: project.name, + clientName: project.client.name, + isBillable: project.billable, + minutesSpent: project.total_hours_logged, + projectMembers: project_members } + end end diff --git a/app/javascript/src/apis/projects.ts b/app/javascript/src/apis/projects.ts index 1df5024481..efc97b7c20 100644 --- a/app/javascript/src/apis/projects.ts +++ b/app/javascript/src/apis/projects.ts @@ -4,6 +4,8 @@ const path = "/projects"; const get = async () => axios.get(`${path}`); -const projects = { get }; +const show = async id => axios.get(`${path}/${id}`); + +const projects = { get, show }; export default projects; diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index ef793b9dd1..3d9654321d 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -10,11 +10,12 @@ const Projects = (isAdminUser) => { const [allProjects, setAllProjects] = React.useState([]); const [showProjectDetails, setShowProjectDetails] = React.useState(null); - const fetchProjects = async () => { - const res = await projects.get(); - if (res.status == 200) { - setAllProjects(res.data.projects); - } + const fetchProjects = () => { + + projects.get() + .then(res => setAllProjects(res.data.projects)) + .catch(err => console.log(err)); + }; React.useEffect(() => { diff --git a/app/models/project.rb b/app/models/project.rb index ee38ef8bee..9aa9e80c40 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -46,6 +46,15 @@ def total_hours_logged(time_frame = "week") timesheet_entries.where(work_date: from..to).sum(:duration) end + def project_member_details + project_members.map do |member| + user = User.find(member.user_id) + { name: user.full_name, + hourly_rate: member.hourly_rate, + minutes_spent: user.timesheet_entries.where(project_id: self.id).sum(:duration) } + end + end + # Move weeK_month_year method copied from client.rb to common place def week_month_year(time_frame) case time_frame diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 33dac524e4..6a18b808a4 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -8,4 +8,8 @@ def index? def create? user_owner_or_admin? end + + def show? + user_owner_or_admin? + end end diff --git a/config/routes/internal_api.rb b/config/routes/internal_api.rb index 41da763bc8..da723efb84 100644 --- a/config/routes/internal_api.rb +++ b/config/routes/internal_api.rb @@ -3,7 +3,7 @@ namespace :internal_api, defaults: { format: "json" } do namespace :v1 do resources :clients, only: [:index, :update, :destroy, :show] - resources :projects, only: [:index] + resources :projects, only: [:index, :show] resources :timesheet_entry, only: [:index, :create, :update, :destroy] resources :reports, only: [:index] resources :workspaces, only: [:update] From ad2f54ae9064213dbf7520402b234bf313459c9b Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Mon, 21 Mar 2022 16:13:50 +0530 Subject: [PATCH 08/21] Render project details on project page Shows the project basic details and the project member details --- .../src/components/Projects/index.tsx | 7 +- .../src/components/Projects/project.tsx | 2 +- .../components/Projects/projectDetails.tsx | 89 ++++++++++++++++++- app/views/projects/index.html.erb | 2 + 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 3d9654321d..838171282f 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -6,7 +6,7 @@ import { Project } from "./project"; import ProjectDetails from "./projectDetails"; import ProjectList from "./projectList"; -const Projects = (isAdminUser) => { +const Projects = ({ editIcon, deleteIcon, isAdminUser }) => { const [allProjects, setAllProjects] = React.useState([]); const [showProjectDetails, setShowProjectDetails] = React.useState(null); @@ -31,7 +31,10 @@ const Projects = (isAdminUser) => { return ( showProjectDetails ? : + id={showProjectDetails} + isAdminUser={isAdminUser} + editIcon={editIcon} + deleteIcon={deleteIcon}/> : ( - <>Showing details for project {id} -); +const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { + const [project, setProject] = React.useState({}); + + const fetchProject = () => { + projectsAPI.show(id) + .then(res => setProject(res.data.project)) + .catch (err => {console.log(err);}); + }; + + React.useEffect(() => { + setAuthHeaders(); + registerIntercepts(); + fetchProject(); + }, []); + + return ( + + <> +
Showing details for project {id}
+
Project-name: {project.name}
+
Project name: {project.name}
+
Client name: {project.clientName}
+
Billable: {project.isBillable}
+
Minutes spent(week): {project.minutesSpent}
+
+
+
+
+ + + + + + + + + + + + + {project?.projectMembers?.map((member, index) => ( + + )) + } + +
+ MEMBER + + HOURLY RATE + + HOURS LOGGED + + COST +
+
+
+
+
+ + + ); + +}; export default ProjectDetails; diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index f4bcd9ed18..3dc9a7596f 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -10,6 +10,8 @@
<%= react_component("Projects", { + editIcon: image_url('edit_image_button.svg'), + deleteIcon: image_url('delete_image_button.svg'), isAdminUser: current_user.has_owner_or_admin_role?(current_user.current_workspace) }) %> From e4521fc9bab34b99bb56906faacafad4ccef6fee Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Thu, 24 Mar 2022 17:16:01 +0530 Subject: [PATCH 09/21] Add react component for member --- .../src/components/Projects/member.tsx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 app/javascript/src/components/Projects/member.tsx diff --git a/app/javascript/src/components/Projects/member.tsx b/app/javascript/src/components/Projects/member.tsx new file mode 100644 index 0000000000..94fb2d1008 --- /dev/null +++ b/app/javascript/src/components/Projects/member.tsx @@ -0,0 +1,87 @@ +import * as React from "react"; +import { minutesToHHMM } from "helpers/hhmm-parser"; + +export interface IMember { + id: number; + name: string; + hourlyRate: number; + minutesSpent: number; + cost: number; + editIcon: string; + deleteIcon: string; + isAdminUser: boolean; + setShowEditDialog: any; + setMemberToEdit: any; + setMemberToDelete: any; + setShowDeleteDialog: any; +} + +export const Member = ({ + id, + name, + hourlyRate, + minutesSpent, + cost, + editIcon, + deleteIcon, + isAdminUser, + setShowEditDialog, + setMemberToEdit, + setMemberToDelete, + setShowDeleteDialog +}: IMember) => { + const [grayColor, setGrayColor] = React.useState(""); + const [isHover, setHover] = React.useState(false); + + const handleMouseEnter = () => { + setGrayColor("bg-miru-gray-100"); + setHover(true); + }; + + const handleMouseLeave = () => { + setGrayColor(""); + setHover(false); + }; + + return ( + + + {name} + + + {hourlyRate} + + + {minutesToHHMM(minutesSpent)} + + + {cost} + + + {isAdminUser && isHover && + } + + + { isAdminUser && isHover && + } + + + ); +}; From 6304e723813d1661e3dfd0a1957c304602094d5e Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Thu, 24 Mar 2022 17:18:12 +0530 Subject: [PATCH 10/21] Fix the suggested changes --- .../internal_api/v1/projects_controller.rb | 12 +---- app/controllers/projects_controller.rb | 2 +- app/javascript/src/apis/projects.ts | 4 +- .../src/components/Projects/index.tsx | 20 ++++---- .../components/Projects/projectDetails.tsx | 48 ++++++++++++++----- .../v1/projects/index.json.jbuilder | 11 +++++ 6 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 app/views/internal_api/v1/projects/index.json.jbuilder diff --git a/app/controllers/internal_api/v1/projects_controller.rb b/app/controllers/internal_api/v1/projects_controller.rb index dce7a3b461..b43426dd31 100644 --- a/app/controllers/internal_api/v1/projects_controller.rb +++ b/app/controllers/internal_api/v1/projects_controller.rb @@ -3,25 +3,17 @@ class InternalApi::V1::ProjectsController < InternalApi::V1::ApplicationController def index authorize Project - - render json: { projects: projects }, status: :ok + render :index, locals: { projects: projects }, status: :ok end def show authorize Project - render json: { project: project }, status: :ok end private def projects - projects = current_user.current_workspace.projects - projects.kept.map { | project | - { id: project.id, - name: project.name, - clientName: project.client.name, - isBillable: project.billable, - minutesSpent: project.total_hours_logged } } + @_projects ||= current_company.projects.kept end def project diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 191738cd64..c307db021e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,7 +4,7 @@ class ProjectsController < ApplicationController skip_after_action :verify_authorized def index - authorize :project + authorize Project end def create diff --git a/app/javascript/src/apis/projects.ts b/app/javascript/src/apis/projects.ts index efc97b7c20..efa38726ee 100644 --- a/app/javascript/src/apis/projects.ts +++ b/app/javascript/src/apis/projects.ts @@ -6,6 +6,6 @@ const get = async () => axios.get(`${path}`); const show = async id => axios.get(`${path}/${id}`); -const projects = { get, show }; +const projectApi = { get, show }; -export default projects; +export default projectApi; diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 838171282f..1e40290831 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -1,21 +1,23 @@ import * as React from "react"; import { ToastContainer } from "react-toastify"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; -import projects from "apis/projects"; -import { Project } from "./project"; +import projectApi from "apis/projects"; +import { IProject } from "./project"; import ProjectDetails from "./projectDetails"; import ProjectList from "./projectList"; const Projects = ({ editIcon, deleteIcon, isAdminUser }) => { - const [allProjects, setAllProjects] = React.useState([]); + const [projects, setProjects] = React.useState([]); const [showProjectDetails, setShowProjectDetails] = React.useState(null); - const fetchProjects = () => { - - projects.get() - .then(res => setAllProjects(res.data.projects)) - .catch(err => console.log(err)); + const fetchProjects = async () => { + try { + const resp = await projectApi.get(); + setProjects(resp.data.projects); + } catch (error) + {console.log(error);} + }; React.useEffect(() => { @@ -36,7 +38,7 @@ const Projects = ({ editIcon, deleteIcon, isAdminUser }) => { editIcon={editIcon} deleteIcon={deleteIcon}/> : ); diff --git a/app/javascript/src/components/Projects/projectDetails.tsx b/app/javascript/src/components/Projects/projectDetails.tsx index 4be584b8c6..7890888e99 100644 --- a/app/javascript/src/components/Projects/projectDetails.tsx +++ b/app/javascript/src/components/Projects/projectDetails.tsx @@ -1,17 +1,39 @@ import * as React from "react"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; -import projectsAPI from "apis/projects"; +import projectAPI from "apis/projects"; import { Member } from "./member"; -import { minutesToHHMM } from "../../helpers/hhmm-parser"; + +export interface IProjectDetails { + id: number; + name: string; + clientName: string; + isBillable: boolean; + minutesSpent: number; + projectMembers: any; + editIcon: string; + deleteIcon: string; + isAdminUser: boolean; + setShowEditDialog: any; + setProjectToEdit: any; + setProjectToDelete: any; + setShowDeleteDialog: any; + projectClickHandler: any; +} const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { - const [project, setProject] = React.useState({}); + const [project, setProject] = React.useState(); + + const fetchProject = async () => { + + try { + const resp = await projectAPI.show(id); + console.log(resp); + setProject(resp.data.project); + } catch (err) { + console.log(err); + } - const fetchProject = () => { - projectsAPI.show(id) - .then(res => setProject(res.data.project)) - .catch (err => {console.log(err);}); }; React.useEffect(() => { @@ -20,15 +42,15 @@ const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { fetchProject(); }, []); + console.log(project); return ( - <>
Showing details for project {id}
-
Project-name: {project.name}
-
Project name: {project.name}
-
Client name: {project.clientName}
-
Billable: {project.isBillable}
-
Minutes spent(week): {project.minutesSpent}
+
Project-name: {project?.name}
+
Project name: {project?.name}
+
Client name: {project?.clientName}
+
Billable: {project?.isBillable}
+
Minutes spent(week): {project?.minutesSpent}
diff --git a/app/views/internal_api/v1/projects/index.json.jbuilder b/app/views/internal_api/v1/projects/index.json.jbuilder new file mode 100644 index 0000000000..92a1504164 --- /dev/null +++ b/app/views/internal_api/v1/projects/index.json.jbuilder @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +json.projects projects do |project| + json.id project.id + json.name project.name + json.client do + json.name project.client.name + end + json.isBillable project.billable + json.minutesSpent project.total_hours_logged +end From d8d0636e85f0bcae4720465a6610336cb4c0cb65 Mon Sep 17 00:00:00 2001 From: Shalaka Patil Date: Fri, 25 Mar 2022 14:26:51 +0530 Subject: [PATCH 11/21] Fix integration for project details page --- .../src/components/Projects/index.tsx | 7 +++--- .../src/components/Projects/member.tsx | 18 +++++++-------- .../components/Projects/projectDetails.tsx | 23 ++++++++----------- app/models/project.rb | 2 +- .../v1/projects/show.json.jbuilder | 9 +++++--- 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 1e40290831..50a5241814 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -1,5 +1,4 @@ import * as React from "react"; -import { ToastContainer } from "react-toastify"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projectApi from "apis/projects"; import { IProject } from "./project"; @@ -16,8 +15,10 @@ const Projects = ({ editIcon, deleteIcon, isAdminUser }) => { const resp = await projectApi.get(); setProjects(resp.data.projects); } catch (error) - {console.log(error);} - + { + // Add error handling + } + }; React.useEffect(() => { diff --git a/app/javascript/src/components/Projects/member.tsx b/app/javascript/src/components/Projects/member.tsx index 94fb2d1008..4defd9efdf 100644 --- a/app/javascript/src/components/Projects/member.tsx +++ b/app/javascript/src/components/Projects/member.tsx @@ -4,9 +4,8 @@ import { minutesToHHMM } from "helpers/hhmm-parser"; export interface IMember { id: number; name: string; - hourlyRate: number; - minutesSpent: number; - cost: number; + hourly_rate: number; + minutes_logged: number; editIcon: string; deleteIcon: string; isAdminUser: boolean; @@ -19,9 +18,8 @@ export interface IMember { export const Member = ({ id, name, - hourlyRate, - minutesSpent, - cost, + hourly_rate, + minutes_logged, editIcon, deleteIcon, isAdminUser, @@ -52,19 +50,19 @@ export const Member = ({ {name} - {hourlyRate} + {hourly_rate} - {minutesToHHMM(minutesSpent)} + {minutesToHHMM(minutes_logged)} - {cost} + {minutes_logged * hourly_rate} {isAdminUser && isHover && +
+ ) +} + +const GetClientBar = ({ data, totalMinutes }:IChartBarGraph) => { + return ( + +

+ TOTAL HOURS: {minutesToHHMM(totalMinutes)} +

+
+ {data.map((element, index) => ) + } +
+
+ + 0 + + + {minutesToHHMM(totalMinutes)} + +
+
+ ); +}; + +export default GetClientBar; diff --git a/app/javascript/src/common/ChartBar/interface.ts b/app/javascript/src/common/ChartBar/interface.ts new file mode 100644 index 0000000000..0d979781f3 --- /dev/null +++ b/app/javascript/src/common/ChartBar/interface.ts @@ -0,0 +1,19 @@ +interface ClientArray { + clients: any; +} + +export interface IChartBar extends ClientArray { + handleSelectChange: any + totalMinutes: number; +} + +export interface ISingleClient { + totalMinutes: number; + index: number; + element: any; +} + +export interface IChartBarGraph { + data: any; + totalMinutes: number; +} diff --git a/app/javascript/src/components/Projects/Details/Table.tsx b/app/javascript/src/components/Projects/Details/Table.tsx new file mode 100644 index 0000000000..24508c3863 --- /dev/null +++ b/app/javascript/src/components/Projects/Details/Table.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { useTable, useRowSelect } from 'react-table'; + +const IndeterminateCheckbox = React.forwardRef( + ({ indeterminate, ...rest }:any, ref) => { + const defaultRef = React.useRef() + const resolvedRef:any = ref || defaultRef + + React.useEffect(() => { + resolvedRef.current.indeterminate = indeterminate + }, [resolvedRef, indeterminate]) + return ( +
+ +
+ + + + + + + + + + + + + + +
+
+ ) + } +) + +const getTableCheckbox = hooks => { + hooks.visibleColumns.push(columns => [ + { + id: 'selection', + Header: ({ getToggleAllRowsSelectedProps }) => ( +
+ +
+ ), + // The cell can use the individual row's getToggleRowSelectedProps method + // to the render a checkbox + Cell: ({ row }) => ( +
+ +
+ ), + }, + ...columns, + ]) +} + +const Table = ({ tableHeader, tableRowArray }) => { + + const data = React.useMemo( + () => tableRowArray, + [] + ) + + const columns = React.useMemo( + () => tableHeader, + [] + ) + + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + selectedFlatRows, + state: { selectedRowIds }, +} = useTable( + { + columns, + data, + }, + useRowSelect, + getTableCheckbox +) + + return ( + <> + + + {headerGroups.map(headerGroup => ( + + {headerGroup.headers.map(column => ( + + ))} + + ))} + + + {rows.slice(0, 10).map((row, i) => { + prepareRow(row) + return ( + + {row.cells.map(cell => { + return + })} + + ) + })} + +
{column.render('Header')}
{cell.render('Cell')}
+ + ); +} + +export default Table; + + +{/*

Selected Rows: {Object.keys(selectedRowIds).length}

+
+  
+    {JSON.stringify(
+      {
+        selectedRowIds: selectedRowIds,
+        'selectedFlatRows[].original': selectedFlatRows.map(
+          d => d.original
+        ),
+      },
+      null,
+      2
+    )}
+  
+
*/} diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx new file mode 100644 index 0000000000..e81273f003 --- /dev/null +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -0,0 +1,144 @@ +import * as React from "react"; +import { setAuthHeaders, registerIntercepts } from "apis/axios"; +import projectAPI from "apis/projects"; +import { Member } from "../member"; +import ChartBar from "../../../common/ChartBar"; +import { unmapper } from '../project.mapper'; +import Table from './Table'; + +export interface IProjectDetails { + id: number; + name: string; + client: any; + is_billable: boolean; + total_minutes_logged: number; + members: any; + editIcon: string; + deleteIcon: string; + isAdminUser: boolean; + setShowEditDialog: any; + setProjectToEdit: any; + setProjectToDelete: any; + setShowDeleteDialog: any; + projectClickHandler: any; +} + +const BannerBox = ({ title, value }) => ( +
  • +

    {title}

    +

    {value}

    +
  • +); + +const getTableHeader = (project) => { + if(project) { + return project.members.map((member, index) => { + const hours = member.minutes/60; + const cost = hours * parseInt(member.hourlyRate); + + return { + col2 :
    ${member.hourlyRate}
    , + col4 :
    ${cost}
    , + col3 :
    {member.minutes}
    , + col1 :
    {member.name}
    + } + }) + } +} + +const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { + + const [project, setProject] = React.useState(); + const fetchProject = async () => { + try { + const resp = await projectAPI.show(id); + setProject(unmapper(resp.data.project_details)); + } catch (err) { + // Add error handling + } + }; + + React.useEffect(() => { + setAuthHeaders(); + registerIntercepts(); + fetchProject(); + }, []); + + + const tableHeader = getTableHeader(project) + + const column = [ + { + Header: 'TEAM MEMBER', + accessor: 'col1', // accessor is the "key" in the data + }, + { + Header: 'HOURLY RATE', + accessor: 'col2', + }, + { + Header: 'HOURS LOGGED', + accessor: 'col3', // accessor is the "key" in the data + }, + { + Header: 'COST', + accessor: 'col4', // accessor is the "key" in the data + }, + ]; + + return ( + <> +
    +
    + +
    + {project && } +
      + + +
    +
    +
    +
    +
    +
    + { project && } + + + + + + ); + +}; +export default ProjectDetails; + + + + + +//
    Showing details for project {id}
    +//
    Project name: {project?.name}
    +//
    Client name: {project?.client.name}
    +//
    Billable: {project?.is_billable}
    +//
    Minutes spent(week): {project?.total_minutes_logged}
    diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 50a5241814..2e686d2dd0 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projectApi from "apis/projects"; import { IProject } from "./project"; -import ProjectDetails from "./projectDetails"; +import ProjectDetails from "./Details"; import ProjectList from "./projectList"; const Projects = ({ editIcon, deleteIcon, isAdminUser }) => { diff --git a/app/javascript/src/components/Projects/project.mapper.ts b/app/javascript/src/components/Projects/project.mapper.ts new file mode 100644 index 0000000000..eda41c93ee --- /dev/null +++ b/app/javascript/src/components/Projects/project.mapper.ts @@ -0,0 +1,20 @@ + +const getMember = (input:any) => { + return input.map((elem) => ({ + hourlyRate: elem.hourly_rate, + id: elem.id, + minutes: elem.minutes_logged, + name: elem.name + })) +} + +export const unmapper = (data:any = {}) => { + return { + totalMinutes: data.total_minutes_logged, + client: data.client, + id: data.id, + is_billable: data.is_billable, + members: getMember(data.members), + name: data.name + } +} diff --git a/app/javascript/src/components/Projects/projectDetails.tsx b/app/javascript/src/components/Projects/projectDetails.tsx deleted file mode 100644 index ead531242a..0000000000 --- a/app/javascript/src/components/Projects/projectDetails.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import * as React from "react"; -import { setAuthHeaders, registerIntercepts } from "apis/axios"; -import projectAPI from "apis/projects"; -import { Member } from "./member"; - -export interface IProjectDetails { - id: number; - name: string; - client: any; - is_billable: boolean; - total_minutes_logged: number; - members: any; - editIcon: string; - deleteIcon: string; - isAdminUser: boolean; - setShowEditDialog: any; - setProjectToEdit: any; - setProjectToDelete: any; - setShowDeleteDialog: any; - projectClickHandler: any; -} - -const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { - - const [project, setProject] = React.useState(); - - const fetchProject = async () => { - - try { - const resp = await projectAPI.show(id); - setProject(resp.data.project_details); - } catch (err) { - // Add error handling - } - - }; - - React.useEffect(() => { - setAuthHeaders(); - registerIntercepts(); - fetchProject(); - }, []); - - return ( - <> -
    Showing details for project {id}
    -
    Project name: {project?.name}
    -
    Client name: {project?.client.name}
    -
    Billable: {project?.is_billable}
    -
    Minutes spent(week): {project?.total_minutes_logged}
    -
    -
    -
    -
    -
    - - - - - - - - - - - - {project?.members?.map((member, index) => ( - - )) - } - -
    - MEMBER - - HOURLY RATE - - HOURS LOGGED - - COST -
    -
    -
    -
    -
    - - - ); - -}; -export default ProjectDetails; diff --git a/app/javascript/stylesheets/application.scss b/app/javascript/stylesheets/application.scss index 8c90931688..cb483a6720 100644 --- a/app/javascript/stylesheets/application.scss +++ b/app/javascript/stylesheets/application.scss @@ -61,10 +61,10 @@ .page-display { &__wrap { - @apply bg-gray-50 p-5 mt-5 w-full flex items-stretch flex-col sm:flex-row text-miru-dark-purple-1000; + @apply mt-5 w-full flex items-stretch flex-col sm:flex-row text-miru-dark-purple-1000; } &__box { - @apply flex flex-col justify-start my-5 pl-8 w-full border-r-0 sm:border-r; + @apply flex flex-col justify-start mr-12 w-full border-r-0 sm:border-r; } &__box:last-child { @apply border-r-0; @@ -194,10 +194,10 @@ @apply min-w-full divide-y divide-gray-200 mt-4; } &__header { - @apply px-6 py-5 text-left font-normal text-xs text-miru-dark-purple-600 tracking-widest; + @apply p-5 text-left font-normal text-xs text-miru-dark-purple-600 tracking-widest; } &__cell { - @apply px-5 py-5 text-left text-xs font-normal text-miru-dark-purple-1000 tracking-normal; + @apply p-5 text-left text-xs font-normal text-miru-dark-purple-1000 tracking-normal; } &__body { @apply bg-white divide-y divide-gray-200; @@ -257,10 +257,12 @@ &__checkbox:checked + div { @apply border-miru-han-purple-1000; } - &__checkbox:checked + div svg { + &__checkbox:checked + div .custom__checkbox-tick { + @apply block; + } + &__checkbox:indeterminate + div .custom__checkbox-dash { @apply block; } - &__radio:checked + label i { @apply bg-miru-han-purple-1000 h-3 w-3; box-shadow: 0px 0px 0px 2px white inset; diff --git a/config/webpack/environment.js b/config/webpack/environment.js index e3e9a6a6be..94d49fe9e9 100644 --- a/config/webpack/environment.js +++ b/config/webpack/environment.js @@ -8,6 +8,8 @@ environment.config.merge({ const aliasConfig = require("./alias"); environment.config.merge(aliasConfig); + + const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ @@ -16,4 +18,11 @@ environment.plugins.prepend('Provide', }) ); -module.exports = environment; +module.exports = environment + +const nodeModulesLoader = environment.loaders.get('nodeModules') + +if (!Array.isArray(nodeModulesLoader.exclude)) { + nodeModulesLoader.exclude = (nodeModulesLoader.exclude == null) ? [] : [nodeModulesLoader.exclude] +} +nodeModulesLoader.exclude.push(/react-table/) diff --git a/package.json b/package.json index 698ec84454..ac7579688d 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-dom": "^17.0.2", "react-router-dom": "^6.1.1", "react-select": "^5.2.2", + "react-table": "^7.7.0", "react-toastify": "^8.1.0", "react-tooltip": "^4.2.21", "react-transition-group": "1.x", diff --git a/yarn.lock b/yarn.lock index e2777f004e..1e5941b01f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7645,6 +7645,11 @@ react-select@^5.2.2: prop-types "^15.6.0" react-transition-group "^4.3.0" +react-table@^7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.7.0.tgz#e2ce14d7fe3a559f7444e9ecfe8231ea8373f912" + integrity sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA== + react-toastify@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-8.1.0.tgz#acaca4e8c4415c8474562dd84a14e6f390ed7f17" From e00270378a08a9d739d39a1b4b0958541958c913 Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Wed, 30 Mar 2022 11:01:54 +0530 Subject: [PATCH 13/21] project details page --- app/javascript/src/common/ChartBar/index.tsx | 50 ++++--- app/javascript/src/common/Table/index.tsx | 104 ++++++++++++++ .../src/components/Projects/Details/Table.tsx | 132 ------------------ .../src/components/Projects/Details/index.tsx | 73 ++++++---- .../{projectList.tsx => List/index.tsx} | 0 .../Projects/{ => List}/project.tsx | 17 +-- .../src/components/Projects/index.tsx | 11 +- .../src/components/Projects/interface.ts | 29 ++++ .../src/components/Projects/member.tsx | 85 ----------- .../src/components/Projects/project.mapper.ts | 20 --- .../components/Projects/projectDetails.tsx | 109 --------------- app/javascript/src/mapper/project.mapper.ts | 16 +++ app/javascript/src/utils/getStatusTag.ts | 2 +- app/views/projects/index.html.erb | 10 -- package.json | 2 +- yarn.lock | 30 ++-- 16 files changed, 241 insertions(+), 449 deletions(-) create mode 100644 app/javascript/src/common/Table/index.tsx delete mode 100644 app/javascript/src/components/Projects/Details/Table.tsx rename app/javascript/src/components/Projects/{projectList.tsx => List/index.tsx} (100%) rename app/javascript/src/components/Projects/{ => List}/project.tsx (84%) create mode 100644 app/javascript/src/components/Projects/interface.ts delete mode 100644 app/javascript/src/components/Projects/member.tsx delete mode 100644 app/javascript/src/components/Projects/project.mapper.ts delete mode 100644 app/javascript/src/components/Projects/projectDetails.tsx create mode 100644 app/javascript/src/mapper/project.mapper.ts diff --git a/app/javascript/src/common/ChartBar/index.tsx b/app/javascript/src/common/ChartBar/index.tsx index 0eabe348a5..b455390506 100644 --- a/app/javascript/src/common/ChartBar/index.tsx +++ b/app/javascript/src/common/ChartBar/index.tsx @@ -21,34 +21,32 @@ const Client = ({ element, totalMinutes, index }:ISingleClient) => {
    - ) -} + ); +}; -const GetClientBar = ({ data, totalMinutes }:IChartBarGraph) => { - return ( - -

    +const GetClientBar = ({ data, totalMinutes }:IChartBarGraph) => ( + +

    TOTAL HOURS: {minutesToHHMM(totalMinutes)} -

    -
    - {data.map((element, index) => ) - } -
    -
    - +

    +
    + {data.map((element, index) => ) + } +
    +
    + 0 - - - {minutesToHHMM(totalMinutes)} - -
    - - ); -}; +
    + + {minutesToHHMM(totalMinutes)} + +
    +
    +); export default GetClientBar; diff --git a/app/javascript/src/common/Table/index.tsx b/app/javascript/src/common/Table/index.tsx new file mode 100644 index 0000000000..8257449a04 --- /dev/null +++ b/app/javascript/src/common/Table/index.tsx @@ -0,0 +1,104 @@ +import React from "react"; +import { useTable, useRowSelect } from "react-table"; + +const IndeterminateCheckbox = React.forwardRef( + ({ indeterminate, ...rest }:any, ref) => { + const defaultRef = React.useRef(); + const resolvedRef:any = ref || defaultRef; + + React.useEffect(() => { + resolvedRef.current.indeterminate = indeterminate; + }, [resolvedRef, indeterminate]); + return ( +
    + +
    + + + + + + + +
    +
    + ); + } +); + +const getTableCheckbox = hooks => { + hooks.visibleColumns.push(columns => [ + { + id: "selection", + Header: ({ getToggleAllRowsSelectedProps }) => ( +
    + +
    + ), + // The cell can use the individual row's getToggleRowSelectedProps method + // to the render a checkbox + Cell: ({ row }) => ( +
    + +
    + ) + }, + ...columns + ]); +}; + +const Table = ({ + tableHeader, + tableRowArray, + hasCheckbox = false +}) => { + + const data = React.useMemo(() => tableRowArray, []); + const columns = React.useMemo(() => tableHeader, []); + + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow + } = useTable( + { + columns, + data + }, + useRowSelect, + hasCheckbox ? getTableCheckbox : () => {} + ); + + return ( + <> + + + {headerGroups.map(headerGroup => ( + + {headerGroup.headers.map(column => { + return ( + + ) + } + )} + + ))} + + + {rows.slice(0, 10).map((row, i) => { + prepareRow(row); + return ( + + {row.cells.map(cell => )} + + ); + })} + +
    {column.render("Header")}
    {cell.render("Cell")}
    + + ); +}; + +export default Table; diff --git a/app/javascript/src/components/Projects/Details/Table.tsx b/app/javascript/src/components/Projects/Details/Table.tsx deleted file mode 100644 index 24508c3863..0000000000 --- a/app/javascript/src/components/Projects/Details/Table.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React from 'react'; -import { useTable, useRowSelect } from 'react-table'; - -const IndeterminateCheckbox = React.forwardRef( - ({ indeterminate, ...rest }:any, ref) => { - const defaultRef = React.useRef() - const resolvedRef:any = ref || defaultRef - - React.useEffect(() => { - resolvedRef.current.indeterminate = indeterminate - }, [resolvedRef, indeterminate]) - return ( -
    - -
    - - - - - - - - - - - - - - -
    -
    - ) - } -) - -const getTableCheckbox = hooks => { - hooks.visibleColumns.push(columns => [ - { - id: 'selection', - Header: ({ getToggleAllRowsSelectedProps }) => ( -
    - -
    - ), - // The cell can use the individual row's getToggleRowSelectedProps method - // to the render a checkbox - Cell: ({ row }) => ( -
    - -
    - ), - }, - ...columns, - ]) -} - -const Table = ({ tableHeader, tableRowArray }) => { - - const data = React.useMemo( - () => tableRowArray, - [] - ) - - const columns = React.useMemo( - () => tableHeader, - [] - ) - - const { - getTableProps, - getTableBodyProps, - headerGroups, - rows, - prepareRow, - selectedFlatRows, - state: { selectedRowIds }, -} = useTable( - { - columns, - data, - }, - useRowSelect, - getTableCheckbox -) - - return ( - <> - - - {headerGroups.map(headerGroup => ( - - {headerGroup.headers.map(column => ( - - ))} - - ))} - - - {rows.slice(0, 10).map((row, i) => { - prepareRow(row) - return ( - - {row.cells.map(cell => { - return - })} - - ) - })} - -
    {column.render('Header')}
    {cell.render('Cell')}
    - - ); -} - -export default Table; - - -{/*

    Selected Rows: {Object.keys(selectedRowIds).length}

    -
    -  
    -    {JSON.stringify(
    -      {
    -        selectedRowIds: selectedRowIds,
    -        'selectedFlatRows[].original': selectedFlatRows.map(
    -          d => d.original
    -        ),
    -      },
    -      null,
    -      2
    -    )}
    -  
    -
    */} diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index e81273f003..e3f21907bd 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -1,10 +1,10 @@ import * as React from "react"; +import { ArrowLeft, DotsThreeVertical } from "phosphor-react"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projectAPI from "apis/projects"; -import { Member } from "../member"; +import Table from "../../../common/Table"; import ChartBar from "../../../common/ChartBar"; -import { unmapper } from '../project.mapper'; -import Table from './Table'; +import { unmapper } from "../../../mapper/project.mapper"; export interface IProjectDetails { id: number; @@ -31,22 +31,22 @@ const BannerBox = ({ title, value }) => ( ); const getTableHeader = (project) => { - if(project) { - return project.members.map((member, index) => { + if (project) { + return project.members.map((member) => { const hours = member.minutes/60; const cost = hours * parseInt(member.hourlyRate); return { - col2 :
    ${member.hourlyRate}
    , - col4 :
    ${cost}
    , - col3 :
    {member.minutes}
    , - col1 :
    {member.name}
    - } - }) + col1:
    {member.name}
    , + col2:
    ${member.hourlyRate}
    , + col3:
    {member.minutes}
    , + col4:
    ${cost}
    + }; + }); } -} +}; -const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { +const ProjectDetails = ({ id }) => { const [project, setProject] = React.useState(); const fetchProject = async () => { @@ -64,30 +64,51 @@ const ProjectDetails = ({ id, editIcon, deleteIcon, isAdminUser }) => { fetchProject(); }, []); - - const tableHeader = getTableHeader(project) + const tableHeader = getTableHeader(project); const column = [ { - Header: 'TEAM MEMBER', - accessor: 'col1', // accessor is the "key" in the data + Header: "TEAM MEMBER", + accessor: "col1", // accessor is the "key" in the data + cssClass: 'abc' }, { - Header: 'HOURLY RATE', - accessor: 'col2', + Header: "HOURLY RATE", + accessor: "col2", + cssClass: 'text-right' }, { - Header: 'HOURS LOGGED', - accessor: 'col3', // accessor is the "key" in the data + Header: "HOURS LOGGED", + accessor: "col3", + cssClass: 'text-right' // accessor is the "key" in the data }, { - Header: 'COST', - accessor: 'col4', // accessor is the "key" in the data - }, + Header: "COST", + accessor: "col4", + cssClass: 'text-right' // accessor is the "key" in the data + } ]; return ( <> +
    +
    +
    + +

    + {project?.name} +

    + + BILLABLE + +
    +
    + +
    +
    +
    @@ -35,8 +37,6 @@ const getTableCheckbox = hooks => {
    ), - // The cell can use the individual row's getToggleRowSelectedProps method - // to the render a checkbox Cell: ({ row }) => (
    @@ -68,7 +68,7 @@ const Table = ({ data }, useRowSelect, - hasCheckbox ? getTableCheckbox : () => {} + hasCheckbox ? getTableCheckbox : {} ); return ( @@ -77,17 +77,15 @@ const Table = ({ {headerGroups.map(headerGroup => ( - {headerGroup.headers.map(column => { - return ( - {column.render("Header")} - ) - } + {headerGroup.headers.map(column => ( + {column.render("Header")} + ) )} ))} - {rows.slice(0, 10).map((row, i) => { + {rows.slice(0, 10).map((row) => { prepareRow(row); return ( diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index e3f21907bd..06ff0a8fff 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -1,9 +1,9 @@ import * as React from "react"; -import { ArrowLeft, DotsThreeVertical } from "phosphor-react"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projectAPI from "apis/projects"; -import Table from "../../../common/Table"; +import { ArrowLeft, DotsThreeVertical } from "phosphor-react"; import ChartBar from "../../../common/ChartBar"; +import Table from "../../../common/Table"; import { unmapper } from "../../../mapper/project.mapper"; export interface IProjectDetails { @@ -70,22 +70,22 @@ const ProjectDetails = ({ id }) => { { Header: "TEAM MEMBER", accessor: "col1", // accessor is the "key" in the data - cssClass: 'abc' + cssClass: "abc" }, { Header: "HOURLY RATE", accessor: "col2", - cssClass: 'text-right' + cssClass: "text-right" }, { Header: "HOURS LOGGED", accessor: "col3", - cssClass: 'text-right' // accessor is the "key" in the data + cssClass: "text-right" // accessor is the "key" in the data }, { Header: "COST", accessor: "col4", - cssClass: 'text-right' // accessor is the "key" in the data + cssClass: "text-right" // accessor is the "key" in the data } ]; @@ -100,9 +100,9 @@ const ProjectDetails = ({ id }) => {

    {project?.name}

    - + BILLABLE - +
    @@ -153,9 +153,3 @@ const ProjectDetails = ({ id }) => { }; export default ProjectDetails; - -//
    Showing details for project {id}
    -//
    Project name: {project?.name}
    -//
    Client name: {project?.client.name}
    -//
    Billable: {project?.is_billable}
    -//
    Minutes spent(week): {project?.total_minutes_logged}
    diff --git a/app/javascript/src/components/Projects/List/project.tsx b/app/javascript/src/components/Projects/List/project.tsx index 0826c6c3ae..3ca5a4e410 100644 --- a/app/javascript/src/components/Projects/List/project.tsx +++ b/app/javascript/src/components/Projects/List/project.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { minutesToHHMM } from "helpers/hhmm-parser"; -import { IProject } from '../interface'; +import { IProject } from "../interface"; export const Project = ({ id, diff --git a/app/javascript/src/components/Projects/index.tsx b/app/javascript/src/components/Projects/index.tsx index 8fa1f47906..0f4e992722 100644 --- a/app/javascript/src/components/Projects/index.tsx +++ b/app/javascript/src/components/Projects/index.tsx @@ -2,9 +2,8 @@ import * as React from "react"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projectApi from "apis/projects"; import ProjectDetails from "./Details"; -import ProjectList from "./List"; import { IProject } from "./interface"; - +import ProjectList from "./List"; const Projects = ({ isAdminUser }) => { const [projects, setProjects] = React.useState([]); @@ -15,11 +14,9 @@ const Projects = ({ isAdminUser }) => { try { const resp = await projectApi.get(); setProjects(resp.data.projects); - } catch (error) - { + } catch (error) { // Add error handling } - }; React.useEffect(() => { diff --git a/app/javascript/src/utils/getStatusTag.ts b/app/javascript/src/utils/getStatusTag.ts index af859cb025..c3150563ca 100644 --- a/app/javascript/src/utils/getStatusTag.ts +++ b/app/javascript/src/utils/getStatusTag.ts @@ -3,7 +3,7 @@ const getStatusCssClass = (status) => { draft: "bg-miru-alert-yellow-400 text-miru-alert-green-1000", overdue: "bg-miru-alert-pink-400 text-miru-alert-red-1000", sent: "bg-miru-alert-green-400 text-miru-alert-green-800", - viewed: "bg-miru-alert-blue-400 text-miru-alert-blue-1000", + viewed: "bg-miru-alert-blue-400 text-miru-alert-blue-1000" }; const lowerCaseStatus = status.toLowerCase(); return `rounded-xl text-xs tracking-widest font-semibold px-1 ${STATUS_LIST[lowerCaseStatus]}`; From 6e0a7d2abf7e74bee077611036007e280b0581dc Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Wed, 30 Mar 2022 20:30:16 +0530 Subject: [PATCH 15/21] bannerbox menu and client name updated --- app/javascript/src/common/AmountBox/index.tsx | 15 +++ app/javascript/src/common/Table/index.tsx | 5 +- .../components/Invoices/BannerBox/index.tsx | 10 -- .../src/components/Invoices/container.tsx | 24 +++-- .../src/components/Projects/Details/index.tsx | 96 +++++++++++-------- app/javascript/stylesheets/application.scss | 7 ++ 6 files changed, 98 insertions(+), 59 deletions(-) create mode 100644 app/javascript/src/common/AmountBox/index.tsx delete mode 100644 app/javascript/src/components/Invoices/BannerBox/index.tsx diff --git a/app/javascript/src/common/AmountBox/index.tsx b/app/javascript/src/common/AmountBox/index.tsx new file mode 100644 index 0000000000..f79370ddc9 --- /dev/null +++ b/app/javascript/src/common/AmountBox/index.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +const AmountBoxContainer = ({ amountBox, cssClass="" }) => ( +
      + { amountBox.map((data, index) => ( +
    • +

      {data.title}

      +

      {data.amount}

      +
    • + ) + )} +
    +); + +export default AmountBoxContainer; diff --git a/app/javascript/src/common/Table/index.tsx b/app/javascript/src/common/Table/index.tsx index ab9f9dae29..0e62bf7f99 100644 --- a/app/javascript/src/common/Table/index.tsx +++ b/app/javascript/src/common/Table/index.tsx @@ -1,8 +1,7 @@ -/* eslint-disable react/display-name */ import React from "react"; import { useTable, useRowSelect } from "react-table"; -const IndeterminateCheckbox = React.forwardRef( +const IndeterminateCheckbox = React.forwardRef( // eslint-disable-line react/display-name ({ indeterminate, ...rest }:any, ref) => { const defaultRef = React.useRef(); const resolvedRef:any = ref || defaultRef; @@ -68,7 +67,7 @@ const Table = ({ data }, useRowSelect, - hasCheckbox ? getTableCheckbox : {} + hasCheckbox ? getTableCheckbox : () => {} // eslint-disable-line @typescript-eslint/no-empty-function ); return ( diff --git a/app/javascript/src/components/Invoices/BannerBox/index.tsx b/app/javascript/src/components/Invoices/BannerBox/index.tsx deleted file mode 100644 index 6ef9270e04..0000000000 --- a/app/javascript/src/components/Invoices/BannerBox/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from "react"; - -const BannerBox = ({ title, value }) => ( -
  • -

    {title}

    -

    {value}

    -
  • -); - -export default BannerBox; diff --git a/app/javascript/src/components/Invoices/container.tsx b/app/javascript/src/components/Invoices/container.tsx index 25fc685f1f..2ab3ca07dd 100644 --- a/app/javascript/src/components/Invoices/container.tsx +++ b/app/javascript/src/components/Invoices/container.tsx @@ -1,8 +1,7 @@ import * as React from "react"; -import BannerBox from "./BannerBox"; +import AmountBoxContainer from "common/AmountBox"; import Table from "./Table"; - const Container = ({ invoiceList, setInvoiceList }) => { const handleSelectAll = (isChecked) => { @@ -20,13 +19,24 @@ const Container = ({ invoiceList, setInvoiceList }) => { setInvoiceList(newInvoiceList); }; + const amountBox = [{ + title: "OVERDUE", + amount: "$35.5k" + }, + { + title: "OUTSTANDING", + amount: "$24.3k" + }, + { + title: "AMOUNT IN DRAFT", + amount: "$24.5k" + }]; + return ( -
      - - - -
    +
    + +
    diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index 06ff0a8fff..977df5f00e 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -1,41 +1,17 @@ import * as React from "react"; import { setAuthHeaders, registerIntercepts } from "apis/axios"; import projectAPI from "apis/projects"; -import { ArrowLeft, DotsThreeVertical } from "phosphor-react"; -import ChartBar from "../../../common/ChartBar"; -import Table from "../../../common/Table"; +import AmountBoxContainer from "common/AmountBox"; +import ChartBar from "common/ChartBar"; +import Table from "common/Table"; +import { ArrowLeft, DotsThreeVertical, Receipt, Pencil, UsersThree, Trash } from "phosphor-react"; import { unmapper } from "../../../mapper/project.mapper"; -export interface IProjectDetails { - id: number; - name: string; - client: any; - is_billable: boolean; - total_minutes_logged: number; - members: any; - editIcon: string; - deleteIcon: string; - isAdminUser: boolean; - setShowEditDialog: any; - setProjectToEdit: any; - setProjectToDelete: any; - setShowDeleteDialog: any; - projectClickHandler: any; -} - -const BannerBox = ({ title, value }) => ( -
  • -

    {title}

    -

    {value}

    -
  • -); - -const getTableHeader = (project) => { +const getTableData = (project) => { if (project) { return project.members.map((member) => { const hours = member.minutes/60; const cost = hours * parseInt(member.hourlyRate); - return { col1:
    {member.name}
    , col2:
    ${member.hourlyRate}
    , @@ -49,6 +25,7 @@ const getTableHeader = (project) => { const ProjectDetails = ({ id }) => { const [project, setProject] = React.useState(); + const [isHeaderMenuVisible, setHeaderMenuVisibility] = React.useState(false); const fetchProject = async () => { try { const resp = await projectAPI.show(id); @@ -64,9 +41,9 @@ const ProjectDetails = ({ id }) => { fetchProject(); }, []); - const tableHeader = getTableHeader(project); + const tableData = getTableData(project); - const column = [ + const tableHeader = [ { Header: "TEAM MEMBER", accessor: "col1", // accessor is the "key" in the data @@ -89,9 +66,24 @@ const ProjectDetails = ({ id }) => { } ]; + const amountBox = [{ + title: "OVERDUE", + amount: "$35.5k" + }, + { + title: "OUTSTANDING", + amount: "$24.3k" + }]; + + const handleMenuVisibility = () => { + setHeaderMenuVisibility(!isHeaderMenuVisible); + }; + + const menuBackground = isHeaderMenuVisible ? "bg-miru-gray-1000" : ""; + return ( <> -
    +
    -
    - +
    + + { isHeaderMenuVisible &&
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    }
    +

    {project && project.client.name}

    @@ -134,16 +155,13 @@ const ProjectDetails = ({ id }) => {
    {project && } -
      - - -
    +
    - { project &&
    } + { project &&
    } diff --git a/app/javascript/stylesheets/application.scss b/app/javascript/stylesheets/application.scss index 855e3dbe1f..04a04e1ee5 100644 --- a/app/javascript/stylesheets/application.scss +++ b/app/javascript/stylesheets/application.scss @@ -42,6 +42,13 @@ } } + .menuButton { + &__wrapper { + box-shadow: -2px 6px 11px grey; + @apply absolute bg-white right-0 p-5 w-64 rounded-xl; + } + } + .header { &__title { @apply text-3xl font-extrabold leading-7 text-gray-900; From 767c724b194053c5118870926e385b6e6bab7d57 Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Mon, 4 Apr 2022 18:12:52 +0530 Subject: [PATCH 16/21] UI review comments resolved --- app/javascript/src/common/ChartBar/index.tsx | 2 +- app/javascript/src/common/Table/index.tsx | 5 ++-- .../src/components/Projects/Details/index.tsx | 24 +++++++++---------- app/javascript/stylesheets/application.scss | 11 +++++---- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/app/javascript/src/common/ChartBar/index.tsx b/app/javascript/src/common/ChartBar/index.tsx index b455390506..5d0741490f 100644 --- a/app/javascript/src/common/ChartBar/index.tsx +++ b/app/javascript/src/common/ChartBar/index.tsx @@ -38,7 +38,7 @@ const GetClientBar = ({ data, totalMinutes }:IChartBarGraph) => ( />) } -
    +
    0 diff --git a/app/javascript/src/common/Table/index.tsx b/app/javascript/src/common/Table/index.tsx index 0e62bf7f99..20d121d04c 100644 --- a/app/javascript/src/common/Table/index.tsx +++ b/app/javascript/src/common/Table/index.tsx @@ -84,10 +84,11 @@ const Table = ({ ))}
    - {rows.slice(0, 10).map((row) => { + {rows.slice(0, 10).map((row, index) => { prepareRow(row); + const isLastChild = rows.length - 1 !== index; return ( - + {row.cells.map(cell => )} ); diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index 977df5f00e..6bc04ce1ad 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -86,10 +86,10 @@ const ProjectDetails = ({ id }) => {
    - -

    +

    {project?.name}

    @@ -97,30 +97,30 @@ const ProjectDetails = ({ id }) => {
    - { isHeaderMenuVisible &&
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • - @@ -128,7 +128,7 @@ const ProjectDetails = ({ id }) => {
    }
    -

    {project && project.client.name}

    +

    {project && project.client.name}

    diff --git a/app/javascript/stylesheets/application.scss b/app/javascript/stylesheets/application.scss index 04a04e1ee5..e02747b52d 100644 --- a/app/javascript/stylesheets/application.scss +++ b/app/javascript/stylesheets/application.scss @@ -44,8 +44,11 @@ .menuButton { &__wrapper { - box-shadow: -2px 6px 11px grey; - @apply absolute bg-white right-0 p-5 w-64 rounded-xl; + box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); + @apply absolute bg-white right-0 py-5 w-64 rounded-xl; + } + &__list-item { + @apply text-sm block hover:bg-miru-gray-100 w-full px-5 py-1.5 text-miru-han-purple-1000 items-center flex; } } @@ -69,10 +72,10 @@ .page-display { &__wrap { - @apply mt-5 w-full flex items-stretch flex-col sm:flex-row text-miru-dark-purple-1000; + @apply pt-5 w-full flex items-stretch flex-col sm:flex-row text-miru-dark-purple-1000 border-t border-miru-gray-1000; } &__box { - @apply flex flex-col justify-start mr-12 w-full border-r-0 sm:border-r; + @apply flex flex-col justify-start mr-12 w-full border-r-0 sm:border-r sm:border-miru-gray-1000; } &__box:last-child { @apply border-r-0; From 9e3e25d99c95621880f8d32bd7030f6165db1c2d Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Mon, 4 Apr 2022 18:16:23 +0530 Subject: [PATCH 17/21] rubocop issues resolved --- app/controllers/internal_api/v1/projects_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/internal_api/v1/projects_controller.rb b/app/controllers/internal_api/v1/projects_controller.rb index 99da9ba01f..4f31ef3bc5 100644 --- a/app/controllers/internal_api/v1/projects_controller.rb +++ b/app/controllers/internal_api/v1/projects_controller.rb @@ -8,10 +8,11 @@ def index def show authorize Project - render :show, locals: { project: project }, status: :ok + render :show, locals: { project: }, status: :ok end private + def projects @_projects ||= current_company.projects.kept end From 19e426eeb3a4accd83589ea3c2db13c3df7b87fa Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Mon, 4 Apr 2022 19:01:05 +0530 Subject: [PATCH 18/21] lint and review comment fixes --- app/javascript/src/common/ChartBar/index.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/javascript/src/common/ChartBar/index.tsx b/app/javascript/src/common/ChartBar/index.tsx index 5d0741490f..ee99fd8979 100644 --- a/app/javascript/src/common/ChartBar/index.tsx +++ b/app/javascript/src/common/ChartBar/index.tsx @@ -19,7 +19,12 @@ const Client = ({ element, totalMinutes, index }:ISingleClient) => {

    {element.name}

    {minutesToHHMM(element.minutes)}

    - +
    ); }; @@ -27,7 +32,7 @@ const Client = ({ element, totalMinutes, index }:ISingleClient) => { const GetClientBar = ({ data, totalMinutes }:IChartBarGraph) => (

    - TOTAL HOURS: {minutesToHHMM(totalMinutes)} + TOTAL HOURS: {minutesToHHMM(totalMinutes)}

    {data.map((element, index) => (
    - 0 + 0 {minutesToHHMM(totalMinutes)} From 2292c0beef04dc292afded9394fe4f1e1dc5ac76 Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Mon, 4 Apr 2022 19:26:46 +0530 Subject: [PATCH 19/21] spacing added --- app/javascript/src/components/Projects/Details/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index 6bc04ce1ad..848191825c 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -89,7 +89,7 @@ const ProjectDetails = ({ id }) => { -

    +

    {project?.name}

    From edec13a21dedaab92609652369b742c7562da66c Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Mon, 4 Apr 2022 20:59:53 +0530 Subject: [PATCH 20/21] cosmetic changes --- .../src/components/Projects/Details/index.tsx | 12 ++++++------ app/javascript/stylesheets/application.scss | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index 848191825c..142d149ae3 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -85,8 +85,8 @@ const ProjectDetails = ({ id }) => { <>
    -
    -

    @@ -96,8 +96,8 @@ const ProjectDetails = ({ id }) => { BILLABLE

    -
    - { isHeaderMenuVisible &&
      @@ -120,7 +120,7 @@ const ProjectDetails = ({ id }) => {
    • - @@ -128,7 +128,7 @@ const ProjectDetails = ({ id }) => {
    }
    -

    {project && project.client.name}

    +

    {project && project.client.name}

    diff --git a/app/javascript/stylesheets/application.scss b/app/javascript/stylesheets/application.scss index e02747b52d..7d6d110e67 100644 --- a/app/javascript/stylesheets/application.scss +++ b/app/javascript/stylesheets/application.scss @@ -42,7 +42,25 @@ } } + .button-icon{ + &__back { + @apply mr-4 h-8 p-1; + } + &__back:hover { + background-color: rgba(205, 214, 223, 0.2); + @apply rounded; + + } + } + .menuButton { + &__button { + @apply rounded px-2 h-8; + } + &__button:hover { + background-color: rgba(205, 214, 223, 0.2); + @apply rounded; + } &__wrapper { box-shadow: 0px 0px 40px rgba(0, 0, 0, 0.1); @apply absolute bg-white right-0 py-5 w-64 rounded-xl; From cde2c9f69669135d9370193620e1851f737d107f Mon Sep 17 00:00:00 2001 From: ajinkyaa Date: Mon, 4 Apr 2022 21:18:45 +0530 Subject: [PATCH 21/21] dropdown updated --- app/javascript/src/components/Projects/Details/index.tsx | 4 ++-- app/javascript/stylesheets/application.scss | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/javascript/src/components/Projects/Details/index.tsx b/app/javascript/src/components/Projects/Details/index.tsx index 142d149ae3..1d0e9ea0b7 100644 --- a/app/javascript/src/components/Projects/Details/index.tsx +++ b/app/javascript/src/components/Projects/Details/index.tsx @@ -84,7 +84,7 @@ const ProjectDetails = ({ id }) => { return ( <>
    -
    +
    -
    +
    diff --git a/app/javascript/stylesheets/application.scss b/app/javascript/stylesheets/application.scss index 7d6d110e67..84c294eb71 100644 --- a/app/javascript/stylesheets/application.scss +++ b/app/javascript/stylesheets/application.scss @@ -49,13 +49,12 @@ &__back:hover { background-color: rgba(205, 214, 223, 0.2); @apply rounded; - } } .menuButton { &__button { - @apply rounded px-2 h-8; + @apply rounded p-2; } &__button:hover { background-color: rgba(205, 214, 223, 0.2);
    {cell.render("Cell")}