fix(timesheet): extend mobile layout to xl and tighten row field widths

This commit is contained in:
2026-04-28 14:21:04 +03:30
parent 5d313a9663
commit 088ad8760b

View File

@@ -1235,11 +1235,11 @@ function EntryEditorFields({
onChange={(projectId) => (onProjectChange ? onProjectChange(projectId) : onChange({ projectId }))} onChange={(projectId) => (onProjectChange ? onProjectChange(projectId) : onChange({ projectId }))}
placeholder={t.timesheet?.projectLabel || "Project"} placeholder={t.timesheet?.projectLabel || "Project"}
portalOwnerId={portalOwnerId} portalOwnerId={portalOwnerId}
className="min-w-0 max-w-[150px] 2xl:max-w-max flex-1" className="min-w-0 max-w-fit flex-1"
/> />
{selectedProject?.client?.name && ( {selectedProject?.client?.name && (
<span className="min-w-0 max-w-[120px] 2xl:max-w-max shrink truncate text-sm text-slate-400 dark:text-slate-500" title={selectedProject.client.name}> <span className="min-w-0 max-w-[120px] 2xl:max-w-fit shrink truncate text-sm text-slate-400 dark:text-slate-500" title={selectedProject.client.name}>
- {selectedProject.client.name} - {selectedProject.client.name}
</span> </span>
)} )}
@@ -1748,7 +1748,7 @@ function MobileRecordedEntryCard({
}; };
return ( return (
<div ref={wrapperRef} className="relative overflow-hidden border-b border-slate-200 bg-slate-100/70 dark:border-slate-800 dark:bg-slate-900/70 md:hidden"> <div ref={wrapperRef} className="relative overflow-hidden border-b border-slate-200 bg-slate-100/70 dark:border-slate-800 dark:bg-slate-900/70 xl:hidden">
<div className="pointer-events-none absolute inset-y-0 left-0 flex w-24 items-center justify-start bg-emerald-500/12 ps-4 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300"> <div className="pointer-events-none absolute inset-y-0 left-0 flex w-24 items-center justify-start bg-emerald-500/12 ps-4 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300">
<Play className="h-4 w-4" /> <Play className="h-4 w-4" />
</div> </div>
@@ -2526,110 +2526,10 @@ export default function Timesheet() {
</div> </div>
</div> </div>
<div
onBlurCapture={handleTimerBlurCapture}
className="mb-4 hidden rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-950 md:block xl:hidden"
>
<div className="space-y-3">
<Input
value={timerDraft.description}
placeholder={t.timesheet?.descriptionPlaceholder || "What are you working on?"}
onChange={(event) => setTimerDraft((current) => ({ ...current, description: event.target.value }))}
disabled={isStartingTimer}
className="h-11 border-slate-200 bg-slate-50 text-sm dark:border-slate-700 dark:bg-slate-900"
/>
<div className="grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(180px,220px)_auto]">
<Select
value={timerDraft.projectId}
onChange={(value) => setTimerDraft((current) => ({ ...current, projectId: String(value) }))}
options={[
{ value: "", label: t.timesheet?.projectLabel || "Project" },
...runningTimerProjects.map((project) => ({ value: project.id, label: project.name })),
]}
className="w-full"
buttonClassName="h-11 w-full rounded-md border border-slate-200 bg-slate-50 px-3 text-sm shadow-none outline-none dark:border-slate-700 dark:bg-slate-900 focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0"
disabled={isStartingTimer}
portalOwnerId={timerEditorOwnerId}
/>
<div className="flex min-w-0 items-center">
<TagMultiSelect
tags={runningTimerTags}
selectedTags={timerDraft.tags}
onToggleTag={(tagId) =>
setTimerDraft((current) => ({ ...current, tags: toggleTagId(current.tags, tagId) }))
}
emptyHint={t.timesheet?.noTagsHint || "Create tags first from the Tags page."}
title={t.tags?.title || "Tags"}
compact
portalOwnerId={timerEditorOwnerId}
className="max-w-full"
buttonClassName="max-w-full"
/>
</div>
<div className="flex items-center justify-start lg:justify-end">
<div className="flex h-11 min-w-[132px] items-center justify-center rounded-md border border-slate-200 bg-slate-50 px-4 text-sm font-semibold text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-white">
{runningEntry ? formatDuration(runningEntry, ticker) : "00:00:00"}
</div>
</div>
</div>
<div className="flex flex-wrap items-center justify-between gap-3">
<BillableIconButton
checked={timerDraft.isBillable}
onChange={(checked) => setTimerDraft((current) => ({ ...current, isBillable: checked }))}
label={t.timesheet?.billable || "Billable"}
disabled={isStartingTimer}
/>
<div className="flex shrink-0 items-center gap-2">
{runningEntry ? (
<>
<Button
variant="destructive"
size="icon"
onClick={() => void handleStop(runningEntry)}
className="h-11 w-11 rounded-md"
title={t.timesheet?.stopTimer || "Stop"}
aria-label={t.timesheet?.stopTimer || "Stop"}
>
<Square className="h-4 w-4 fill-current" />
</Button>
<Button
variant="secondary"
size="icon"
onClick={openDiscardTimerModal}
disabled={isDiscardingTimer}
className="h-11 w-11 rounded-md"
title={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
aria-label={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
>
{isDiscardingTimer ? "..." : <Trash2 className="h-4 w-4" />}
</Button>
</>
) : (
<Button
onClick={() => void handleStartTimer()}
disabled={isStartingTimer}
size="icon"
className="h-11 w-11 rounded-md"
title={t.timesheet?.startTimer || "Start"}
aria-label={t.timesheet?.startTimer || "Start"}
>
{isStartingTimer ? "..." : <Play className="h-4 w-4 fill-current" />}
</Button>
)}
</div>
</div>
</div>
</div>
<div <div
ref={mobileTimerRef} ref={mobileTimerRef}
onBlurCapture={handleTimerBlurCapture} onBlurCapture={handleTimerBlurCapture}
className="mb-4 rounded-xl border border-slate-200 bg-white p-3 shadow-sm dark:border-slate-800 dark:bg-slate-950 md:hidden" className="mb-4 rounded-xl border border-slate-200 bg-white p-3 shadow-sm dark:border-slate-800 dark:bg-slate-950 xl:hidden"
> >
<div className="space-y-3"> <div className="space-y-3">
<Input <Input
@@ -2802,20 +2702,7 @@ export default function Timesheet() {
lang={lang} lang={lang}
/> />
</div> </div>
<div className="hidden md:block xl:hidden"> <div className="xl:hidden">
<RecordedEntryCard
entry={entry}
t={t}
projects={projects}
tags={tags}
onDelete={openDeleteModal}
onRestart={handleRestartFromEntry}
onEntryUpdated={handleEntryUpdated}
variant="tablet"
lang={lang}
/>
</div>
<div className="md:hidden">
<MobileRecordedEntryCard <MobileRecordedEntryCard
entry={entry} entry={entry}
t={t} t={t}