Skip to content

Commit 6a61d1e

Browse files
committed
feat(events): add support for events in journal view
1 parent 6ebeeea commit 6a61d1e

19 files changed

Lines changed: 900 additions & 43 deletions

File tree

env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ export default defineConfig({
3737
APP_UMAMI_SRC: Schema.string.optional(),
3838
APP_UMAMI_ID: Schema.string.optional(),
3939
APP_SENTRY_DSN: Schema.string.optional(),
40+
APP_GOOGLE_OAUTH_CLIENT_ID: Schema.string.optional(),
4041
},
4142
});

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@codemirror/lang-markdown": "^6.2.5",
2424
"@dnd-kit/core": "^6.1.0",
2525
"@dnd-kit/sortable": "^8.0.0",
26+
"@react-oauth/google": "^0.13.4",
2627
"@replit/codemirror-vim": "^6.2.1",
2728
"@sentry/react": "^8.26.0",
2829
"@togglecorp/fujs": "^2.2.0",
@@ -50,7 +51,6 @@
5051
"@typescript-eslint/eslint-plugin": "^5.59.5",
5152
"@typescript-eslint/parser": "^5.59.5",
5253
"@vite-pwa/assets-generator": "^1.0.0",
53-
"sharp": "^0.33.5",
5454
"@vitejs/plugin-react-swc": "^3.5.0",
5555
"@vitest/coverage-v8": "^1.2.2",
5656
"autoprefixer": "^10.4.14",
@@ -74,6 +74,7 @@
7474
"postcss-normalize": "^10.0.1",
7575
"postcss-preset-env": "^8.3.2",
7676
"rollup-plugin-visualizer": "^5.9.0",
77+
"sharp": "^0.33.5",
7778
"stylelint": "^16.7.0",
7879
"stylelint-config-concentric": "^2.0.2",
7980
"stylelint-config-recommended": "^14.0.1",

pnpm-lock.yaml

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const gqlClient = new UrqlClient({
3333
PublicQuery: () => null,
3434
AppEnumCollection: () => null,
3535
DailyStandUpType: () => null,
36+
DailyHoursType: () => null,
3637
AppEnumCollectionTimeEntryType: (item) => String(item.key),
3738
AppEnumCollectionTimeEntryStatus: (item) => String(item.key),
3839
AppEnumCollectionJournalLeaveType: (item) => String(item.key),

src/components/MonthlyCalendar/index.tsx

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,25 @@ import {
1414
encodeDate,
1515
} from '@togglecorp/fujs';
1616

17+
import AvailabilityIndicator from '#components/AvailabilityIndicator';
1718
import Button from '#components/Button';
1819
import DateContext from '#contexts/date';
20+
import {
21+
type JournalLeaveTypeEnum,
22+
type JournalWorkFromHomeTypeEnum,
23+
} from '#generated/types/graphql';
1924

2025
import styles from './styles.module.css';
2126

27+
export interface DateInfo {
28+
totalMinutes?: number;
29+
targetMinutes?: number;
30+
isHoliday?: boolean;
31+
leaveType?: JournalLeaveTypeEnum | null;
32+
wfhType?: JournalWorkFromHomeTypeEnum | null;
33+
hasEvent?: boolean;
34+
}
35+
2236
const dateFormatter = new Intl.DateTimeFormat(
2337
[],
2438
{
@@ -51,19 +65,22 @@ interface Props {
5165
initialYear: number;
5266
initialMonth: number;
5367
onDateClick?: (date: string) => void;
68+
onMonthChange?: (year: number, month: number) => void;
69+
dateInfoMap?: Map<string, DateInfo>;
5470
componentRef?: React.MutableRefObject<{
5571
resetView: (year: number, month: number) => void;
5672
} | null>;
5773
}
5874

59-
// TODO: Show holidays, leaves on calendar
6075
function MonthlyCalendar(props: Props) {
6176
const {
6277
initialYear,
6378
initialMonth,
6479
componentRef,
6580
className,
6681
onDateClick,
82+
onMonthChange,
83+
dateInfoMap,
6784
weekDayNameClassName,
6885
dateClassName,
6986
selectedDate,
@@ -90,6 +107,10 @@ function MonthlyCalendar(props: Props) {
90107
}
91108
}, [componentRef, resetView]);
92109

110+
useEffect(() => {
111+
onMonthChange?.(year, month);
112+
}, [onMonthChange, year, month]);
113+
93114
const handlePrevMonth = useCallback(
94115
() => {
95116
const newMonth = month - 1;
@@ -181,18 +202,35 @@ function MonthlyCalendar(props: Props) {
181202
))}
182203
{daysInMonth.map((day) => {
183204
const date = encodeDate(new Date(year, month, day.date));
184-
let variant;
185-
if (fullDate === date) {
186-
variant = 'secondary' as const;
187-
} else if (selectedDate === date) {
188-
variant = 'quaternary' as const;
189-
} else {
190-
variant = 'transparent' as const;
191-
}
205+
206+
const info = dateInfoMap?.get(date);
207+
const isPast = date <= fullDate;
208+
const targetMinutes = info?.targetMinutes ?? 0;
209+
const totalMinutes = info?.totalMinutes ?? 0;
210+
const fillPct = isPast && targetMinutes > 0
211+
? Math.min(1, totalMinutes / targetMinutes)
212+
: 0;
213+
const hue = Math.round(fillPct * 120);
214+
const hoursBg = isPast && targetMinutes > 0
215+
? `hsla(${hue}, 55%, 50%, 0.18)`
216+
: undefined;
217+
218+
// Treat holidays as full leave for AvailabilityIndicator
219+
const effectiveLeaveType: JournalLeaveTypeEnum | null = info?.isHoliday
220+
? 'FULL'
221+
: (info?.leaveType ?? null);
222+
const effectiveWfhType = info?.wfhType ?? null;
223+
const hasAvailability = effectiveLeaveType != null || effectiveWfhType != null;
224+
192225
return (
193226
<Button
194227
onClick={onDateClick}
195-
className={_cs(styles.date, dateClassName)}
228+
className={_cs(
229+
styles.date,
230+
fullDate === date && styles.today,
231+
selectedDate === date && styles.selected,
232+
dateClassName,
233+
)}
196234
name={date}
197235
title="Set date from calendar"
198236
key={day.date}
@@ -201,10 +239,23 @@ function MonthlyCalendar(props: Props) {
201239
// Note +2 is due to the week day name row
202240
gridRowStart: day.week + 2,
203241
opacity: day.dayOfWeek === 6 || day.dayOfWeek === 0 ? 0.5 : 1,
242+
backgroundColor: hoursBg,
204243
}}
205-
variant={variant}
244+
variant="transparent"
206245
>
207-
{day.date}
246+
<span className={styles.dateContent}>
247+
{day.date}
248+
{info?.hasEvent && (
249+
<span className={styles.eventDot} />
250+
)}
251+
{hasAvailability && (
252+
<AvailabilityIndicator
253+
className={styles.availabilityIndicator}
254+
leaveType={effectiveLeaveType}
255+
wfhType={effectiveWfhType}
256+
/>
257+
)}
258+
</span>
208259
</Button>
209260
);
210261
})}

src/components/MonthlyCalendar/styles.module.css

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,50 @@
1717
.monthly-calendar {
1818
display: grid;
1919
grid-template-columns: repeat(7, 1fr);
20-
gap: var(--spacing-xs);
20+
gap: 1px;
21+
align-items: stretch;
2122

2223
.day-name {
2324
padding: var(--spacing-xs);
25+
text-align: center;
2426
color: var(--color-text-light);
2527
font-size: var(--font-size-md);
2628
}
2729

2830
.date {
29-
padding: var(--spacing-xs) var(--spacing-xs);
31+
transition: background-color 0.3s ease;
32+
padding: var(--spacing-2xs);
33+
width: 100%;
34+
height: 100%;
35+
min-height: 2.5rem;
3036
font-size: var(--font-size-sm);
3137
font-weight: unset;
38+
39+
.date-content {
40+
display: flex;
41+
align-items: center;
42+
flex-direction: column;
43+
width: 100%;
44+
height: 100%;
45+
text-align: center;
46+
line-height: 1;
47+
gap: 1px;
48+
}
49+
50+
.event-dot {
51+
border-radius: 50%;
52+
background-color: var(--color-primary);
53+
width: 0.3em;
54+
height: 0.3em;
55+
}
56+
57+
.availability-indicator {
58+
position: absolute;
59+
top: var(--spacing-3xs);
60+
right: var(--spacing-3xs);
61+
font-size: 0.5em;
62+
gap: var(--spacing-4xs);
63+
}
3264
}
3365
}
3466
}

0 commit comments

Comments
 (0)