Skip to content

Commit

Permalink
"working-hours" module: add metadata field, update overwork math
Browse files Browse the repository at this point in the history
  • Loading branch information
ba1uev committed Aug 10, 2024
1 parent 39f5438 commit f6d145c
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 201 deletions.
5 changes: 3 additions & 2 deletions src/client/utils/portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import * as React from 'react'
import MC from '#client/components/__import-components'
import { ComponentRef } from '#shared/types'

export const getComponentInstance = (cr: ComponentRef): React.FC<any> | null => {
export const getComponentInstance = (
cr: ComponentRef
): React.FC<any> | null => {
const moduleId = cr[0] as keyof typeof MC
const moduleComponents = MC[moduleId]
if (!moduleComponents) return null
Expand All @@ -26,4 +28,3 @@ export const renderComponent =
}
return <Component key={`${cr[0]}_${cr[1]}`} {...props} />
}

1 change: 1 addition & 0 deletions src/modules/_template/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"dependencies": ["<DEPENDENCY_MODULE_ID>"],
"requiredIntegrations": [],
"recommendedIntegrations": [],
"availableCronJobs": [],
"models": [],
"clientRouter": {
"public": {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import * as React from 'react'
import dayjs, { Dayjs } from 'dayjs'
import config from '#client/config'
import {
Button,
H1,
Icons,
Placeholder,
RoundButton,
Select,
Expand Down Expand Up @@ -222,7 +220,7 @@ export const AdminWorkingHours: React.FC = () => {
!!x.overworkLevel &&
!!x.overworkTime && (
<Tag size="small" color={x.overworkLevel}>
Overwork {getDurationString(x.overworkTime)}
Additional {getDurationString(x.overworkTime)}
</Tag>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const DefaultEntriesModal: React.FC<DefaultEntriesModalProps> = ({
}, [newEntryTime])

return (
<Modal title="Your default working hours" onClose={onClose}>
<Modal title="Your default working schedule" onClose={onClose}>
<div className="flex flex-col gap-y-6">
<div>
<P>Specify your usual working schedule and use it for prefilling.</P>
Expand Down Expand Up @@ -136,11 +136,7 @@ export const DefaultEntriesModal: React.FC<DefaultEntriesModalProps> = ({
</div>
)}
{!showNewEntryInput ? (
<FButton
kind="secondary"
onClick={onAddEntry}
className="w-full mb-2"
>
<FButton kind="primary" onClick={onAddEntry} className="w-full mb-2">
{sortedEntries.length ? 'Add one more entry' : 'Add entry'}
</FButton>
) : (
Expand Down
143 changes: 0 additions & 143 deletions src/modules/working-hours/client/components/EntryRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,15 @@ import * as React from 'react'
import {
FButton,
Icons,
Modal,
P,
RoundButton,
TimeRangePicker,
showNotification,
} from '#client/components/ui'
import * as fp from '#shared/utils/fp'
import { cn } from '#client/utils'
import {
DefaultWorkingHoursEntry,
DefaultWorkingHoursEntryUpdateRequest,
WorkingHoursConfig,
WorkingHoursEntry,
WorkingHoursEntryUpdateRequest,
} from '#shared/types'
import {
useDefaultEntries,
useCreateDefaultEntry,
useDeleteDefaultEntry,
useUpdateDefaultEntry,
} from '../queries'
import { formatTimeString } from '../helpers'

type EntryRowProps = {
entry: WorkingHoursEntry | DefaultWorkingHoursEntry
Expand Down Expand Up @@ -97,133 +84,3 @@ export const EntryRow: React.FC<EntryRowProps> = ({
</div>
)
}

type DefaultEntriesModalProps = {
onClose: () => void
moduleConfig: WorkingHoursConfig
refetchModuleConfig: () => void
}
const DefaultEntriesModal: React.FC<DefaultEntriesModalProps> = ({
onClose,
moduleConfig,
refetchModuleConfig,
}) => {
const newEntryRef = React.useRef<HTMLDivElement>(null)
const [showNewEntryInput, setShowNewEntryInput] = React.useState(false)
const [newEntryTime, setNewEntryTime] = React.useState<[string, string]>([
'',
'',
])

const { data: entries = [], refetch: refetchDefaultEntries } =
useDefaultEntries()
const refetch = () => {
refetchDefaultEntries()
refetchModuleConfig()
showNotification('Your default working hours have changed', 'success')
}
const { mutate: createDefaultEntry } = useCreateDefaultEntry(refetch)
const { mutate: updateDefaultEntry } = useUpdateDefaultEntry(refetch)
const { mutate: deleteDefaultEntry } = useDeleteDefaultEntry(refetch)

const sortedEntries = React.useMemo(() => {
return entries.sort(
fp.sortWith((x) => {
const [h, m] = x.startTime.split(':').map(Number)
return h * 60 + m
})
)
}, [entries])

const onAddEntry = React.useCallback(() => {
setShowNewEntryInput(!showNewEntryInput)
if (!showNewEntryInput) {
setTimeout(() => {
const firstTimeInput: HTMLInputElement =
newEntryRef.current?.querySelector('input[type="time"]')!
if (firstTimeInput) firstTimeInput.focus()
}, 100)
}
}, [showNewEntryInput])
const onChangeNewEntryTime = React.useCallback((from: string, to: string) => {
setNewEntryTime([from, to])
}, [])
const onSaveNewEntry = React.useCallback(() => {
createDefaultEntry({
startTime: newEntryTime[0],
endTime: newEntryTime[1],
})
setShowNewEntryInput(false)
setNewEntryTime(['', ''])
}, [newEntryTime])

return (
<Modal title="Your default working hours" onClose={onClose}>
<div className="flex flex-col gap-y-6">
<div>
<P>Specify your usual working schedule and use it for prefilling.</P>
{!entries.length && (
<P className="text-text-tertiary">
If your default working hours are not set, the following will be
used:{' '}
{moduleConfig.defaultEntries
.map(
(x) => `${formatTimeString(x[0])} - ${formatTimeString(x[1])}`
)
.join(', ')}
.
</P>
)}
</div>
{(!!sortedEntries.length || showNewEntryInput) && (
<div className="flex flex-col gap-y-1">
{sortedEntries.map((x) => (
<EntryRow
key={x.id}
entry={x}
updateEntry={updateDefaultEntry}
deleteEntry={deleteDefaultEntry}
editable={true}
/>
))}
{showNewEntryInput && (
<div
ref={newEntryRef}
className={cn('flex gap-x-1 items-center')}
>
<Icons.EntryArrow
fillClassName="fill-fill-18"
className="-mt-1 mr-1 hidden sm:block"
/>
<TimeRangePicker
from={newEntryTime[0]}
to={newEntryTime[1]}
inputClassName="px-0 py-[8px] w-28 text-center no-input-buttons"
onChange={onChangeNewEntryTime}
/>
<FButton kind="primary" size="small" onClick={onSaveNewEntry}>
Save
</FButton>
<RoundButton
onClick={() => setShowNewEntryInput(false)}
icon="Cross"
/>
</div>
)}
</div>
)}
{!showNewEntryInput ? (
<FButton
kind="secondary"
onClick={onAddEntry}
className="w-full mb-2"
>
{sortedEntries.length ? 'Add one more entry' : 'Add entry'}
</FButton>
) : (
<div />
)}
</div>
</Modal>
)
}
47 changes: 41 additions & 6 deletions src/modules/working-hours/client/components/WorkingHoursEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as React from 'react'
import dayjs from 'dayjs'
import {
ComponentWrapper,
FButton,
HR,
HeaderWrapper,
Icons,
LoaderSpinner,
} from '#client/components/ui'
import { renderMarkdown } from '#client/utils/markdown'
import { WorkingHoursEditorWeek } from './WorkingHoursEditorWeek'
import { WorkingHoursEditorMonth } from './WorkingHoursEditorMonth'
import { DefaultEntriesModal } from './DefaultEntriesModal'
Expand All @@ -16,7 +19,16 @@ import { useConfig } from '../queries'
import { formatTimeString } from '../helpers'

export const WorkingHoursEditor: React.FC = () => {
const [viewMode, setViewMode] = React.useState<'week' | 'month'>('week')
const [viewMode, setViewMode] = React.useState<'week' | 'month'>(
(() => {
const url = new URL(window.location.href)
const view = url.searchParams.get('view')
if (view === 'month') {
return 'month'
}
return 'week'
})()
)
const [showDefaultEntriesModal, setShowDefaultEntriesModal] =
React.useState(false)

Expand All @@ -34,20 +46,43 @@ export const WorkingHoursEditor: React.FC = () => {

React.useEffect(() => {
const url = new URL(window.location.href)
if (url.searchParams.get('view') === 'month') {
setViewMode('month')
const view = url.searchParams.get('view')
if (view !== viewMode) {
url.searchParams.set('view', viewMode)
window.history.replaceState({}, '', url.toString())
}
}, [])
}, [viewMode])

return !!moduleConfig ? (
<div className="flex flex-col gap-y-2">
{/* header */}
<ComponentWrapper>
<HeaderWrapper title="Working Hours">
{moduleConfig.policyText && (
<>
<div
className="phq_markdown-content "
dangerouslySetInnerHTML={{
__html: renderMarkdown(moduleConfig.policyText),
}}
/>
<HR className="my-6" />
</>
)}

<div className="my-4 text-text-tertiary">
Your agreed working week: {moduleConfig.weeklyWorkingHours}h (
{moduleConfig.workingDays
.map((x) => dayjs().day(x).format('dddd'))
.join(', ')}
)
</div>

{/* {moduleConfig.policyText && <></>} */}
<div>
{moduleConfig.personalDefaultEntries.length ? (
<div className="-my-2">
Your default working hours:{' '}
Your schedule:{' '}
{moduleConfig.personalDefaultEntries
.sort(
fp.sortWith((x) => {
Expand Down Expand Up @@ -76,7 +111,7 @@ export const WorkingHoursEditor: React.FC = () => {
onClick={() => setShowDefaultEntriesModal(true)}
className="w-full"
>
Configure your default working hours
Configure your default working schedule
</FButton>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export const WorkingHoursEditorMonth: React.FC<{
{getDurationString(x.workingHours)}
{!!x.overworkLevel && !!x.overworkTime && (
<Tag size="small" color={x.overworkLevel}>
Overwork {getDurationString(x.overworkTime)}
Additional {getDurationString(x.overworkTime)}
</Tag>
)}
</div>
Expand Down Expand Up @@ -402,14 +402,14 @@ export const WorkingHoursEditorMonth: React.FC<{
</div>
</div>
<div className="rounded-tiny bg-bg-primary flex flex-col gap-y-4 mb-4">
<div>Agreed working week: {moduleConfig.weeklyWorkingHours}h</div>
{/* <div>Agreed working week: {moduleConfig.weeklyWorkingHours}h</div> */}
<div>
<Input
name="show_entries"
type="checkbox"
checked={showEntries}
onChange={(v) => setShowEntries(Boolean(v))}
inlineLabel="Show More Details"
inlineLabel="Show more details"
/>
</div>
<div className="-mx-6 sm:-mx-8">
Expand All @@ -434,7 +434,7 @@ export const WorkingHoursEditorMonth: React.FC<{
: '–',
},
{
Header: 'Overwork',
Header: 'Additional hours',
accessor: () =>
totalPerMonth.overworkTime
? getDurationString(totalPerMonth.overworkTime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export const WorkingHoursUserModal: React.FC<{
{getDurationString(x.workingHours)}
{!!x.overworkLevel && !!x.overworkTime && (
<Tag size="small" color={x.overworkLevel}>
Overwork {getDurationString(x.overworkTime)}
Additional {getDurationString(x.overworkTime)}
</Tag>
)}
</div>
Expand Down Expand Up @@ -347,7 +347,7 @@ export const WorkingHoursUserModal: React.FC<{
type="checkbox"
checked={showEntries}
onChange={(v) => setShowEntries(Boolean(v))}
inlineLabel="Show More Details"
inlineLabel="Show more details"
/>
</div>
<div>
Expand Down
1 change: 1 addition & 0 deletions src/modules/working-hours/metadata-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const roleConfigSchema = z
nextWeeks: z.number().min(0).max(18),
}),
publicHolidayCalendarId: z.string().optional(),
policyText: z.string().optional(),
})
.strict()

Expand Down
Loading

0 comments on commit f6d145c

Please sign in to comment.