From cb8c2ed31cd4356da533d9357f538acca0709dcd Mon Sep 17 00:00:00 2001 From: tnagorra Date: Sun, 20 Oct 2024 09:54:40 +0545 Subject: [PATCH 1/3] Update margins/gaps for header in journal view - Add border around select input tag element - Increment size of tags: h1, h2, h3, h4 - Highlight errored items --- src/components/SelectInputContainer/index.tsx | 1 + src/index.css | 28 +++++------ .../DailyJournal/AddWorkItemDialog/index.tsx | 2 +- src/views/DailyJournal/DayView/index.tsx | 10 +++- .../DailyJournal/DayView/styles.module.css | 47 ++++++++++++++----- src/views/DailyJournal/index.tsx | 25 ++++------ 6 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/components/SelectInputContainer/index.tsx b/src/components/SelectInputContainer/index.tsx index 028b4ff..192dda8 100644 --- a/src/components/SelectInputContainer/index.tsx +++ b/src/components/SelectInputContainer/index.tsx @@ -316,6 +316,7 @@ function SelectInputContainer< style={(searchText || !valueDisplay) ? undefined : { backgroundColor: valueBgColor ?? colorscheme[0][1], color: valueFgColor ?? colorscheme[0][0], + border: 'var(--width-separator-sm) solid var(--color-foreground)', }} id={inputId} name={name} diff --git a/src/index.css b/src/index.css index 959ae23..acb3396 100644 --- a/src/index.css +++ b/src/index.css @@ -23,15 +23,15 @@ --base-font-size: 1rem; - --font-size-2xs: calc(var(--base-font-size) * 0.625); - --font-size-xs: calc(var(--base-font-size) * 0.75); - --font-size-sm: calc(var(--base-font-size) * 0.875); - --font-size-md: var(--base-font-size); - --font-size-lg: calc(var(--base-font-size) * 1.25); - --font-size-xl: calc(var(--base-font-size) * 1.44); - --font-size-2xl: calc(var(--base-font-size) * 1.8); - --font-size-3xl: calc(var(--base-font-size) * 2.5); - --font-size-4xl: calc(var(--base-font-size) * 3.6); + --font-size-2xs: calc(var(--base-font-size) * 0.625); /* -1 */ + --font-size-xs: calc(var(--base-font-size) * 0.75); /* -0.5 */ + --font-size-sm: calc(var(--base-font-size) * 0.875); /* -0.25 */ + --font-size-md: var(--base-font-size); /* 0 */ + --font-size-lg: calc(var(--base-font-size) * 1.25); /* 0.5 */ + --font-size-xl: calc(var(--base-font-size) * 1.44); /* 1 */ + --font-size-2xl: calc(var(--base-font-size) * 1.8); /* 1.5 */ + --font-size-3xl: calc(var(--base-font-size) * 2.5); /* 2 */ + --font-size-4xl: calc(var(--base-font-size) * 3.6); /* 2.5 */ @media screen and (max-width: 40rem) { --base-font-size: 0.875rem; @@ -165,23 +165,23 @@ h1, h2, h3, h4, h5, h6 { } h1 { - font-size: var(--font-size-2xl); + font-size: var(--font-size-3xl); } h2 { - font-size: var(--font-size-xl); + font-size: var(--font-size-2xl); } h3 { - font-size: var(--font-size-lg); + font-size: var(--font-size-xl); } h4 { - font-size: var(--font-size-md); + font-size: var(--font-size-lg); } h5 { - font-size: var(--font-size-sm); + font-size: var(--font-size-md); } p { diff --git a/src/views/DailyJournal/AddWorkItemDialog/index.tsx b/src/views/DailyJournal/AddWorkItemDialog/index.tsx index 089de64..5cf6029 100644 --- a/src/views/DailyJournal/AddWorkItemDialog/index.tsx +++ b/src/views/DailyJournal/AddWorkItemDialog/index.tsx @@ -88,7 +88,7 @@ function AddWorkItemDialog(props: Props) { )} item.date === selectedDate); - const entriesWithoutTask = filteredWorkItems - .filter((item) => isNotDefined(item.type) && item.status !== 'TODO') - .length; - const entriesWithoutHours = filteredWorkItems - .filter((item) => isNotDefined(item.duration) && item.status !== 'TODO') - .length; + const entriesWithError = filteredWorkItems + .filter((item) => ( + item.status !== 'TODO' && ( + isNotDefined(item.type) + || isNotDefined(item.duration) + ) + )).length; const leaveType = myTimeEntriesResult.data?.private.journal?.leaveType; const wfhType = myTimeEntriesResult.data?.private.journal?.wfhType; @@ -677,19 +678,11 @@ export function Component() { > - {entriesWithoutTask > 0 && ( + {entriesWithError > 0 && (
- {`${entriesWithoutTask} uncategorized`} - -
- )} - {entriesWithoutHours > 0 && ( -
- - - {`${entriesWithoutHours} untracked`} + {`${entriesWithError} issues`}
)} From d17fcfe8fcb45535ed6b95469c42c6c158c654f4 Mon Sep 17 00:00:00 2001 From: tnagorra Date: Tue, 22 Oct 2024 14:38:52 +0545 Subject: [PATCH 2/3] Add collapsible groups in Journal - Update keys for the grouped items in Journal - Add settings link on the sidebar in Journal --- src/utils/common.ts | 24 ++++--- src/views/DailyJournal/DayView/index.tsx | 65 +++++++++++++++++-- .../DailyJournal/DayView/styles.module.css | 6 +- src/views/DailyJournal/StartSidebar/index.tsx | 7 ++ 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/utils/common.ts b/src/utils/common.ts index 654d975..5d53fa1 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -255,6 +255,7 @@ type GroupedItem = { attribute: ATTRIBUTE; level: number; } | { + itemKey: string; type: 'list-item'; value: LIST_ITEM; level: number; @@ -273,18 +274,21 @@ export function groupListByAttributes( const groupedItems = list.flatMap((listItem, listIndex) => { if (listIndex === 0) { - const groupKey = getGroupKey(listItem, attributes); - const headings = attributes.map((attribute, i) => ({ - type: 'heading' as const, - value: listItem, - attribute, - level: i, - groupKey, - })); + const headings = attributes.map((attribute, i) => { + const groupKey = getGroupKey(listItem, attributes.slice(0, i + 1)); + return { + type: 'heading' as const, + value: listItem, + attribute, + level: i, + groupKey, + }; + }); return [ ...headings, { + itemKey: getGroupKey(listItem, attributes), type: 'list-item' as const, value: listItem, level: attributes.length, @@ -306,6 +310,7 @@ export function groupListByAttributes( if (attributeMismatchIndex === -1) { return [ { + itemKey: getGroupKey(listItem, attributes), type: 'list-item' as const, value: listItem, level: attributes.length, @@ -313,12 +318,12 @@ export function groupListByAttributes( ]; } - const groupKey = getGroupKey(listItem, attributes); const headings = attributes.map((attribute, i) => { if (i < attributeMismatchIndex) { return undefined; } + const groupKey = getGroupKey(listItem, attributes.slice(0, i + 1)); return { type: 'heading' as const, value: listItem, @@ -331,6 +336,7 @@ export function groupListByAttributes( return [ ...headings, { + itemKey: getGroupKey(listItem, attributes), type: 'list-item' as const, value: listItem, level: attributes.length, diff --git a/src/views/DailyJournal/DayView/index.tsx b/src/views/DailyJournal/DayView/index.tsx index 9a9c493..36d5b83 100644 --- a/src/views/DailyJournal/DayView/index.tsx +++ b/src/views/DailyJournal/DayView/index.tsx @@ -4,7 +4,12 @@ import { useCallback, useContext, useMemo, + useState, } from 'react'; +import { + RiArrowDownSLine, + RiArrowUpSLine, +} from 'react-icons/ri'; import { _cs, bound, @@ -14,6 +19,7 @@ import { sum, } from '@togglecorp/fujs'; +import Button from '#components/Button'; import DefaultMessage from '#components/DefaultMessage'; import Indent from '#components/Indent'; import EnumsContext from '#contexts/enums'; @@ -69,6 +75,20 @@ function DayView(props: Props) { const { taskById } = useContext(EnumsContext); + const [groupVisibility, setGroupVisibility] = useState([]); + + const toggleGroupVisibility = useCallback( + (value: string) => { + setGroupVisibility((prevValues) => { + if (!prevValues.includes(value)) { + return [...prevValues, value]; + } + return prevValues.filter((prevValue) => prevValue !== value); + }); + }, + [], + ); + const [storedConfig] = useLocalStorage('timur-config'); const getWorkItemLabelFromAttr = useCallback(( @@ -157,7 +177,7 @@ function DayView(props: Props) { ), ); - return groupListByAttributes( + const result = groupListByAttributes( sortedWorkItems, dailyJournalAttributeOrder.slice(0, groupLevel), (a, b, attr) => { @@ -171,6 +191,7 @@ function DayView(props: Props) { return values; }, ); + return result; }, [ taskById, workItems, @@ -209,6 +230,14 @@ function DayView(props: Props) {
{groupedItems.map((groupedItem) => { if (groupedItem.type === 'heading') { + const hidden = groupVisibility.some((groupKey) => ( + groupedItem.groupKey !== groupKey + && groupedItem.groupKey.startsWith(groupKey) + )); + if (hidden) { + return null; + } + // Main Heading // NOTE: Need to add 1 as groupLevel and level starts from 1 and 0 resp. if (groupedItem.level + 1 < (groupLevel - joinLevel + 1)) { @@ -224,9 +253,10 @@ function DayView(props: Props) { const headingLevel = bound(groupedItem.level + 2, 2, 4); const Heading = `h${headingLevel}` as unknown as ElementType; + const key = `heading-${groupedItem.groupKey}`; return ( {indent && } @@ -238,6 +268,16 @@ function DayView(props: Props) { /> )} {headingText} + ); } @@ -246,10 +286,11 @@ function DayView(props: Props) { // NOTE: We only need to show one subheading after the main headings // NOTE: Need to add 1 as groupLevel and level starts from 1 and 0 resp. if (groupedItem.level + 1 === groupLevel) { + const key = `sub-heading-${groupedItem.groupKey}`; return (

{indent && ( + {i > (groupLevel - joinLevel) && (
)} @@ -290,6 +331,16 @@ function DayView(props: Props) { ); })} +

); } @@ -301,6 +352,12 @@ function DayView(props: Props) { if (!taskDetails) { return null; } + const hidden = groupVisibility.some( + (groupKey) => groupedItem.itemKey.startsWith(groupKey), + ); + if (hidden) { + return null; + } const itemErrored = groupedItem.value.status !== 'TODO' && ( isNotDefined(groupedItem.value.type) diff --git a/src/views/DailyJournal/DayView/styles.module.css b/src/views/DailyJournal/DayView/styles.module.css index cc0f6be..f7f0734 100644 --- a/src/views/DailyJournal/DayView/styles.module.css +++ b/src/views/DailyJournal/DayView/styles.module.css @@ -11,7 +11,7 @@ .heading { display: flex; - align-items: baseline; + align-items: center; flex-grow: 1; flex-wrap: wrap; gap: 0 var(--spacing-sm); @@ -47,7 +47,7 @@ .nested-heading { display: flex; - align-items: baseline; + align-items: center; gap: var(--spacing-sm); color: var(--color-text-light); font-weight: var(--font-weight-semibold); @@ -77,7 +77,7 @@ .joined-heading { display: flex; - align-items: baseline; + align-items: center; gap: var(--spacing-sm); color: var(--color-text-light); font-weight: var(--font-weight-semibold); diff --git a/src/views/DailyJournal/StartSidebar/index.tsx b/src/views/DailyJournal/StartSidebar/index.tsx index c2507b0..5b73ec3 100644 --- a/src/views/DailyJournal/StartSidebar/index.tsx +++ b/src/views/DailyJournal/StartSidebar/index.tsx @@ -25,6 +25,7 @@ import { isNotDefined, } from '@togglecorp/fujs'; +import Link from '#components/Link'; import MonthlyCalendar from '#components/MonthlyCalendar'; import RadioInput from '#components/RadioInput'; import DateContext from '#contexts/date'; @@ -282,6 +283,12 @@ function StartSidebar(props: Props) { labelSelector={numericOptionLabelSelector} />
+ + Other Settings + ); } From 3f0f4812a4b545a9421fe476f0b021158c875975 Mon Sep 17 00:00:00 2001 From: tnagorra Date: Tue, 22 Oct 2024 15:31:33 +0545 Subject: [PATCH 3/3] Add checkbox to enable experiment: collapsible group - Add collapsed groups to local storage - Clear collapsed groups when grouping or ordering is changed --- src/App/index.tsx | 34 +++++++- src/utils/constants.ts | 2 + src/utils/types.ts | 2 + src/views/DailyJournal/DayView/index.tsx | 105 +++++++++++++---------- src/views/Settings/index.tsx | 6 ++ 5 files changed, 103 insertions(+), 46 deletions(-) diff --git a/src/App/index.tsx b/src/App/index.tsx index c350438..27138d4 100644 --- a/src/App/index.tsx +++ b/src/App/index.tsx @@ -159,10 +159,40 @@ function App() { }, }); + const handleStorageStateUpdate: typeof setStorageState = useCallback( + (val) => { + setStorageState((prevValue) => { + const newValue = typeof val === 'function' + ? val(prevValue) + : val; + + if ( + prevValue['timur-config'].value?.dailyJournalGrouping !== newValue['timur-config'].value?.dailyJournalGrouping + || prevValue['timur-config'].value?.dailyJournalAttributeOrder !== newValue['timur-config'].value?.dailyJournalAttributeOrder + ) { + const overriddenValue: typeof newValue = { + ...newValue, + 'timur-config': { + ...newValue['timur-config'], + value: { + ...(newValue['timur-config'].value ?? defaultConfigValue), + collapsedGroups: [], + }, + }, + }; + return overriddenValue; + } + + return newValue; + }); + }, + [], + ); + const storageContextValue = useMemo(() => ({ storageState, - setStorageState, - }), [storageState]); + setStorageState: handleStorageStateUpdate, + }), [storageState, handleStorageStateUpdate]); // Device Size diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 352e9ff..1e65564 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -10,6 +10,7 @@ export const defaultConfigValue: ConfigStorage = { indent: true, compactTextArea: false, checkboxForStatus: false, + enableCollapsibleGroups: false, startSidebarShown: window.innerWidth >= 900, endSidebarShown: false, dailyJournalGrouping: { @@ -22,6 +23,7 @@ export const defaultConfigValue: ConfigStorage = { { key: 'task', sortDirection: 1 }, { key: 'status', sortDirection: 1 }, ], + collapsedGroups: [], }; export const colorscheme: [string, string][] = [ diff --git a/src/utils/types.ts b/src/utils/types.ts index 5c31fa9..c390fd0 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -42,10 +42,12 @@ export type ConfigStorage = { checkboxForStatus: boolean, compactTextArea: boolean, indent: boolean, + enableCollapsibleGroups: boolean, dailyJournalAttributeOrder: DailyJournalAttribute[]; dailyJournalGrouping: DailyJournalGrouping; + collapsedGroups: string[], startSidebarShown: boolean, endSidebarShown: boolean, } diff --git a/src/views/DailyJournal/DayView/index.tsx b/src/views/DailyJournal/DayView/index.tsx index 36d5b83..1540470 100644 --- a/src/views/DailyJournal/DayView/index.tsx +++ b/src/views/DailyJournal/DayView/index.tsx @@ -4,7 +4,6 @@ import { useCallback, useContext, useMemo, - useState, } from 'react'; import { RiArrowDownSLine, @@ -75,21 +74,42 @@ function DayView(props: Props) { const { taskById } = useContext(EnumsContext); - const [groupVisibility, setGroupVisibility] = useState([]); + const [ + storedConfig, + setStoredConfig, + ] = useLocalStorage('timur-config'); - const toggleGroupVisibility = useCallback( + const handleToggleCollapseGroup = useCallback( (value: string) => { - setGroupVisibility((prevValues) => { + setStoredConfig((prevConfig) => { + // FIXME: We should not need to add fallback + const prevValues = prevConfig.collapsedGroups ?? []; + if (!prevValues.includes(value)) { - return [...prevValues, value]; + return { + ...prevConfig, + collapsedGroups: [...prevValues, value], + }; } - return prevValues.filter((prevValue) => prevValue !== value); + return { + ...prevConfig, + collapsedGroups: prevValues.filter((prevValue) => prevValue !== value), + }; }); }, - [], + [setStoredConfig], ); - const [storedConfig] = useLocalStorage('timur-config'); + const { + dailyJournalAttributeOrder, + dailyJournalGrouping: { + groupLevel, + joinLevel, + }, + indent, + enableCollapsibleGroups, + collapsedGroups, + } = storedConfig; const getWorkItemLabelFromAttr = useCallback(( item: WorkItem, @@ -151,15 +171,6 @@ function DayView(props: Props) { [workItems], ); - const { - dailyJournalAttributeOrder, - dailyJournalGrouping: { - groupLevel, - joinLevel, - }, - indent, - } = storedConfig; - const groupedItems = useMemo(() => { if (isNotDefined(taskById) || isNotDefined(workItems)) { return []; @@ -230,10 +241,11 @@ function DayView(props: Props) {
{groupedItems.map((groupedItem) => { if (groupedItem.type === 'heading') { - const hidden = groupVisibility.some((groupKey) => ( - groupedItem.groupKey !== groupKey - && groupedItem.groupKey.startsWith(groupKey) - )); + const hidden = enableCollapsibleGroups + && collapsedGroups.some((groupKey) => ( + groupedItem.groupKey !== groupKey + && groupedItem.groupKey.startsWith(groupKey) + )); if (hidden) { return null; } @@ -268,16 +280,18 @@ function DayView(props: Props) { /> )} {headingText} - + {enableCollapsibleGroups && ( + + )} ); } @@ -331,16 +345,18 @@ function DayView(props: Props) { ); })} - + {enableCollapsibleGroups && ( + + )} ); } @@ -352,9 +368,10 @@ function DayView(props: Props) { if (!taskDetails) { return null; } - const hidden = groupVisibility.some( - (groupKey) => groupedItem.itemKey.startsWith(groupKey), - ); + const hidden = enableCollapsibleGroups + && collapsedGroups.some( + (groupKey) => groupedItem.itemKey.startsWith(groupKey), + ); if (hidden) { return null; } diff --git a/src/views/Settings/index.tsx b/src/views/Settings/index.tsx index d5a4841..8e9d8b8 100644 --- a/src/views/Settings/index.tsx +++ b/src/views/Settings/index.tsx @@ -91,6 +91,12 @@ export function Component() { value={storedConfig.indent} onChange={setConfigFieldValue} /> +