forked from aws-samples/amplify-next-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: group projects by accounts in weekly planning and work context
- Loading branch information
Carsten Koch
committed
Oct 14, 2024
1 parent
09a654a
commit ca03fc6
Showing
11 changed files
with
542 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import MakeProjectDecision from "@/components/planning/MakeProjectDecision"; | ||
import { usePlanningProjectFilter } from "@/components/planning/usePlanningProjectFilter"; | ||
import { Accordion } from "@/components/ui/accordion"; | ||
import { useWeekPlanContext } from "./useWeekPlanContext"; | ||
|
||
const PlanWeekContextNotWork = () => { | ||
const { weekPlan, startDate } = useWeekPlanContext(); | ||
const { projects, saveProjectDates } = usePlanningProjectFilter(); | ||
|
||
return ( | ||
weekPlan && ( | ||
<Accordion type="single" collapsible> | ||
{projects.map((project) => ( | ||
<MakeProjectDecision | ||
startDate={startDate} | ||
key={project.id} | ||
isInFocus={weekPlan.projectIds.some((id) => id === project.id)} | ||
project={project} | ||
saveOnHoldDate={(onHoldTill) => | ||
saveProjectDates({ projectId: project.id, onHoldTill }) | ||
} | ||
/> | ||
))} | ||
</Accordion> | ||
) | ||
); | ||
}; | ||
|
||
export default PlanWeekContextNotWork; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { make2YearsRevenueText } from "@/helpers/projects"; | ||
import { Loader2 } from "lucide-react"; | ||
import ApiLoadingError from "../layouts/ApiLoadingError"; | ||
import DefaultAccordionItem from "../ui-elements/accordion/DefaultAccordionItem"; | ||
import { Accordion } from "../ui/accordion"; | ||
import MakeProjectDecision from "./MakeProjectDecision"; | ||
import { | ||
usePlanAccountProjects, | ||
withPlanAccountProjects, | ||
} from "./usePlanAccountProjects"; | ||
import { useWeekPlanContext } from "./useWeekPlanContext"; | ||
|
||
const PlanWeekContextWork = () => { | ||
const { weekPlan, startDate } = useWeekPlanContext(); | ||
const { accountsProjects, loadingAccounts, errorAccounts, saveProjectDates } = | ||
usePlanAccountProjects(); | ||
|
||
return ( | ||
<div className="space-y-6"> | ||
<ApiLoadingError error={errorAccounts} title="Error loading accounts" /> | ||
|
||
{loadingAccounts && ( | ||
<Loader2 className="mt-2 ml-2 h-6 w-6 animate-spin" /> | ||
)} | ||
|
||
<Accordion type="single" collapsible> | ||
{accountsProjects?.map(({ id, name, pipeline, projects }) => ( | ||
<DefaultAccordionItem | ||
key={id} | ||
triggerTitle={name} | ||
triggerSubTitle={[ | ||
`${projects.length} projects`, | ||
make2YearsRevenueText(pipeline), | ||
]} | ||
value={id} | ||
> | ||
<Accordion type="single" collapsible> | ||
{projects.map((project) => ( | ||
<MakeProjectDecision | ||
startDate={startDate} | ||
key={project.id} | ||
isInFocus={weekPlan?.projectIds.some( | ||
(id) => id === project.id | ||
)} | ||
project={project} | ||
saveOnHoldDate={(onHoldTill) => | ||
saveProjectDates({ projectId: project.id, onHoldTill }) | ||
} | ||
/> | ||
))} | ||
</Accordion> | ||
</DefaultAccordionItem> | ||
))} | ||
</Accordion> | ||
</div> | ||
); | ||
}; | ||
|
||
export default withPlanAccountProjects(PlanWeekContextWork); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import ButtonGroup from "@/components/ui-elements/btn-group/btn-group"; | ||
import { Label } from "@/components/ui/label"; | ||
import { projectFilters, ProjectFilters } from "@/helpers/planning"; | ||
import { usePlanningProjectFilter } from "./usePlanningProjectFilter"; | ||
|
||
const PlanWeekFilter = () => { | ||
const { projectFilter, setProjectFilter } = usePlanningProjectFilter(); | ||
|
||
return ( | ||
<div className="space-y-2"> | ||
<Label htmlFor="project-filter" className="mx-2 font-semibold"> | ||
Filter projects | ||
</Label> | ||
<ButtonGroup | ||
elementId="project-filter" | ||
values={["Open", "In Focus", "On Hold"]} | ||
selectedValue={projectFilter} | ||
onSelect={(val: string) => | ||
projectFilters.includes(val as ProjectFilters) && | ||
setProjectFilter(val as ProjectFilters) | ||
} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
export default PlanWeekFilter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import DateSelector from "@/components/ui-elements/selectors/date-selector"; | ||
import { Button } from "@/components/ui/button"; | ||
import { Label } from "@/components/ui/label"; | ||
import { CalendarCheck, Loader2, Play } from "lucide-react"; | ||
import { useWeekPlanContext } from "./useWeekPlanContext"; | ||
|
||
const PlanWeekForm = () => { | ||
const { | ||
weekPlan, | ||
startDate, | ||
setStartDate, | ||
isLoading, | ||
confirmProjectSelection, | ||
createWeekPlan, | ||
} = useWeekPlanContext(); | ||
|
||
return ( | ||
<div className="space-y-2"> | ||
<Label htmlFor="week-start-date" className="font-semibold"> | ||
Start date of the week | ||
</Label> | ||
<DateSelector | ||
disabled={!!weekPlan} | ||
elementId="week-start-date" | ||
date={startDate} | ||
setDate={setStartDate} | ||
/> | ||
{isLoading ? ( | ||
<Loader2 className="mt-2 ml-2 h-6 w-6 animate-spin" /> | ||
) : weekPlan ? ( | ||
<Button onClick={confirmProjectSelection}> | ||
<div className="flex flex-row gap-2 items-center"> | ||
<CalendarCheck className="w-4 h-4" /> Confirm Project Selection | ||
</div> | ||
</Button> | ||
) : ( | ||
<Button onClick={() => createWeekPlan(startDate)}> | ||
<div className="flex flex-row gap-2 items-center"> | ||
<Play className="w-4 h-4" /> Start Week Planning | ||
</div> | ||
</Button> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default PlanWeekForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { usePlanningProjectFilter } from "./usePlanningProjectFilter"; | ||
import { useWeekPlanContext } from "./useWeekPlanContext"; | ||
|
||
const PlanWeekStatistics = () => { | ||
const { weekPlan } = useWeekPlanContext(); | ||
const { openProjectsCount, onholdProjectsCount, focusProjectsCount } = | ||
usePlanningProjectFilter(); | ||
|
||
return ( | ||
<div className="mx-2 md:mx-4 my-8 font-semibold text-sm text-muted-foreground md:text-center"> | ||
{!weekPlan ? ( | ||
"Start Week Planning to review a list of projects for the current context." | ||
) : ( | ||
<div> | ||
<div> | ||
Review each project and decide if you can make progress here during | ||
the next week. | ||
</div> | ||
<div>Projects to be reviewed: {openProjectsCount}</div> | ||
<div>Projects on hold: {onholdProjectsCount}</div> | ||
<div>Projects in focus: {focusProjectsCount}</div> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default PlanWeekStatistics; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { useAccountsContext } from "@/api/ContextAccounts"; | ||
import { | ||
AccountProjects, | ||
mapAccountOrder, | ||
mapAccountProjects, | ||
} from "@/helpers/planning"; | ||
import { filter, flow, map, sortBy } from "lodash/fp"; | ||
import { | ||
ComponentType, | ||
createContext, | ||
FC, | ||
useContext, | ||
useEffect, | ||
useState, | ||
} from "react"; | ||
import { usePlanningProjectFilter } from "./usePlanningProjectFilter"; | ||
|
||
interface PlanAccountProjectsType { | ||
accountsProjects: AccountProjects[]; | ||
loadingAccounts: boolean; | ||
errorAccounts: any; | ||
saveProjectDates: (props: { | ||
projectId: string; | ||
dueDate?: Date; | ||
doneOn?: Date; | ||
onHoldTill?: Date | null; | ||
}) => Promise<string | undefined>; | ||
} | ||
|
||
const PlanAccountProjects = createContext<PlanAccountProjectsType | null>(null); | ||
|
||
export const usePlanAccountProjects = () => { | ||
const searchContext = useContext(PlanAccountProjects); | ||
if (!searchContext) | ||
throw new Error( | ||
"usePlanAccountProjects must be used within PlanAccountProjectsProvider" | ||
); | ||
return searchContext; | ||
}; | ||
|
||
interface PlanAccountProjectsProviderProps { | ||
children: React.ReactNode; | ||
} | ||
|
||
export const PlanAccountProjectsProvider: FC< | ||
PlanAccountProjectsProviderProps | ||
> = ({ children }) => { | ||
const { accounts, loadingAccounts, errorAccounts } = useAccountsContext(); | ||
const { projects, saveProjectDates } = usePlanningProjectFilter(); | ||
const [accountsProjects, setAccountsProjects] = useState<AccountProjects[]>( | ||
[] | ||
); | ||
|
||
useEffect(() => { | ||
flow( | ||
map(mapAccountProjects(projects)), | ||
filter(({ projects }) => projects.length > 0), | ||
map(mapAccountOrder), | ||
sortBy((a) => -a.order), | ||
setAccountsProjects | ||
)(accounts); | ||
}, [accounts, projects]); | ||
|
||
return ( | ||
<PlanAccountProjects.Provider | ||
value={{ | ||
accountsProjects, | ||
loadingAccounts, | ||
errorAccounts, | ||
saveProjectDates, | ||
}} | ||
> | ||
{children} | ||
</PlanAccountProjects.Provider> | ||
); | ||
}; | ||
|
||
export function withPlanAccountProjects<Props extends object>( | ||
Component: ComponentType<Props> | ||
) { | ||
return function WrappedProvider(componentProps: Props) { | ||
return ( | ||
<PlanAccountProjectsProvider> | ||
<Component {...componentProps} /> | ||
</PlanAccountProjectsProvider> | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { useAccountsContext } from "@/api/ContextAccounts"; | ||
import { Project, useProjectsContext } from "@/api/ContextProjects"; | ||
import { | ||
filterAndSortProjectsForWeeklyPlanning, | ||
ProjectFilters, | ||
setProjectsFilterCount, | ||
} from "@/helpers/planning"; | ||
import { flow } from "lodash/fp"; | ||
import { createContext, FC, useContext, useEffect, useState } from "react"; | ||
import { useWeekPlanContext } from "./useWeekPlanContext"; | ||
|
||
interface PlanningProjectFilterType { | ||
projects: Project[]; | ||
projectFilter: ProjectFilters; | ||
setProjectFilter: (filt: ProjectFilters) => void; | ||
openProjectsCount: number; | ||
focusProjectsCount: number; | ||
onholdProjectsCount: number; | ||
saveProjectDates: (props: { | ||
projectId: string; | ||
dueDate?: Date; | ||
doneOn?: Date; | ||
onHoldTill?: Date | null; | ||
}) => Promise<string | undefined>; | ||
} | ||
|
||
const PlanningProjectFilter = createContext<PlanningProjectFilterType | null>( | ||
null | ||
); | ||
|
||
export const usePlanningProjectFilter = () => { | ||
const searchContext = useContext(PlanningProjectFilter); | ||
if (!searchContext) | ||
throw new Error( | ||
"usePlanningProjectFilter must be used within PlanningProjectFilterProvider" | ||
); | ||
return searchContext; | ||
}; | ||
|
||
interface PlanningProjectFilterProviderProps { | ||
children: React.ReactNode; | ||
} | ||
|
||
export const PlanningProjectFilterProvider: FC< | ||
PlanningProjectFilterProviderProps | ||
> = ({ children }) => { | ||
const { projects, saveProjectDates } = useProjectsContext(); | ||
const { accounts } = useAccountsContext(); | ||
const { weekPlan, startDate } = useWeekPlanContext(); | ||
const [projectFilter, setProjectFilter] = useState<ProjectFilters>("Open"); | ||
const [filteredAndSortedProjects, setFilteredAndSortedProjects] = useState( | ||
filterAndSortProjectsForWeeklyPlanning( | ||
accounts, | ||
startDate, | ||
weekPlan, | ||
projectFilter | ||
)(projects) | ||
); | ||
const [openProjectsCount, setOpenProjectsCount] = useState(0); | ||
const [focusProjectsCount, setFocusProjectsCount] = useState(0); | ||
const [onholdProjectsCount, setOnholdProjectsCount] = useState(0); | ||
|
||
useEffect(() => { | ||
flow( | ||
filterAndSortProjectsForWeeklyPlanning( | ||
accounts, | ||
startDate, | ||
weekPlan, | ||
projectFilter | ||
), | ||
setFilteredAndSortedProjects | ||
)(projects); | ||
}, [accounts, projectFilter, projects, startDate, weekPlan]); | ||
|
||
useEffect(() => { | ||
setProjectsFilterCount( | ||
projects, | ||
accounts, | ||
startDate, | ||
weekPlan, | ||
setOpenProjectsCount, | ||
setFocusProjectsCount, | ||
setOnholdProjectsCount | ||
); | ||
}, [accounts, projects, startDate, weekPlan]); | ||
|
||
return ( | ||
<PlanningProjectFilter.Provider | ||
value={{ | ||
projects: filteredAndSortedProjects, | ||
projectFilter, | ||
setProjectFilter, | ||
openProjectsCount, | ||
onholdProjectsCount, | ||
focusProjectsCount, | ||
saveProjectDates, | ||
}} | ||
> | ||
{children} | ||
</PlanningProjectFilter.Provider> | ||
); | ||
}; |
Oops, something went wrong.