add print buttons to mobile

This commit is contained in:
Sean Sube 2025-06-14 23:09:12 -05:00
parent 95a3a08492
commit dec4b848b6
No known key found for this signature in database
GPG Key ID: 3EED7B957D362AF1
6 changed files with 70 additions and 26 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title> <title>Task Receipts</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -89,6 +89,7 @@ export function App() {
onTaskSelect={setSelectedTask} onTaskSelect={setSelectedTask}
onStepSelect={setSelectedStep} onStepSelect={setSelectedStep}
onBack={handleBack} onBack={handleBack}
onPrintTask={handlePrintTask}
onPrintStep={handlePrintStep} onPrintStep={handlePrintStep}
onAddNote={handleAddNote} onAddNote={handleAddNote}
/> />

View File

@ -125,7 +125,7 @@ export function DesktopLayout({
}} }}
> >
<Box onClick={() => onStepSelect(step)} sx={{ flex: 1 }}> <Box onClick={() => onStepSelect(step)} sx={{ flex: 1 }}>
<Typography>{step.name}</Typography> <Typography>{step.order}: {step.name}</Typography>
</Box> </Box>
{onPrintStep && selectedUser && ( {onPrintStep && selectedUser && (
<IconButton <IconButton

View File

@ -3,6 +3,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import PrintIcon from '@mui/icons-material/Print'; import PrintIcon from '@mui/icons-material/Print';
import type { GroupWithTasks, TaskWithSteps, StepWithNotes } from '../types'; import type { GroupWithTasks, TaskWithSteps, StepWithNotes } from '../types';
import { CreateButtons } from '../components/CreateButtons'; import { CreateButtons } from '../components/CreateButtons';
import { useUserSelection } from '../hooks/useUserSelection';
function isGroupView(selectedGroup?: GroupWithTasks) { function isGroupView(selectedGroup?: GroupWithTasks) {
return !selectedGroup; return !selectedGroup;
@ -16,6 +17,10 @@ function isStepView(selectedTask?: TaskWithSteps, selectedStep?: StepWithNotes)
return selectedTask && !selectedStep; return selectedTask && !selectedStep;
} }
function isGroupWithTasks(group: any): group is GroupWithTasks {
return group && typeof group.id === 'string' && Array.isArray(group.tasks);
}
export function MobileLayout({ export function MobileLayout({
groups = [], groups = [],
selectedGroup, selectedGroup,
@ -25,6 +30,7 @@ export function MobileLayout({
onTaskSelect, onTaskSelect,
onStepSelect, onStepSelect,
onBack, onBack,
onPrintTask,
onPrintStep, onPrintStep,
onAddNote onAddNote
}: { }: {
@ -36,9 +42,11 @@ export function MobileLayout({
onTaskSelect: (task: TaskWithSteps | undefined) => void; onTaskSelect: (task: TaskWithSteps | undefined) => void;
onStepSelect: (step: StepWithNotes | undefined) => void; onStepSelect: (step: StepWithNotes | undefined) => void;
onBack: () => void; onBack: () => void;
onPrintTask?: (taskId: string, userId: string) => void;
onPrintStep?: (stepId: string, userId: string) => void; onPrintStep?: (stepId: string, userId: string) => void;
onAddNote?: (content: string) => void; onAddNote?: (content: string) => void;
}) { }) {
const { selectedUser } = useUserSelection();
const groupList: GroupWithTasks[] = groups; const groupList: GroupWithTasks[] = groups;
const taskList: TaskWithSteps[] = (selectedGroup?.tasks as TaskWithSteps[]) ?? []; const taskList: TaskWithSteps[] = (selectedGroup?.tasks as TaskWithSteps[]) ?? [];
const stepList: StepWithNotes[] = (selectedTask?.steps as StepWithNotes[]) ?? []; const stepList: StepWithNotes[] = (selectedTask?.steps as StepWithNotes[]) ?? [];
@ -61,6 +69,14 @@ export function MobileLayout({
<ArrowBackIcon /> <ArrowBackIcon />
</IconButton> </IconButton>
<Typography variant="h6">{selectedTask.name}</Typography> <Typography variant="h6">{selectedTask.name}</Typography>
{onPrintTask && selectedUser && (
<IconButton
onClick={() => onPrintTask(String(selectedTask.id), String(selectedUser.id))}
sx={{ ml: 'auto' }}
>
<PrintIcon />
</IconButton>
)}
</Box> </Box>
)} )}
@ -70,9 +86,9 @@ export function MobileLayout({
<ArrowBackIcon /> <ArrowBackIcon />
</IconButton> </IconButton>
<Typography variant="h6">{selectedStep.name}</Typography> <Typography variant="h6">{selectedStep.name}</Typography>
{onPrintStep && ( {onPrintStep && selectedUser && (
<IconButton <IconButton
onClick={() => onPrintStep(String(selectedStep.id), '1')} // TODO: Get real user ID onClick={() => onPrintStep(String(selectedStep.id), String(selectedUser.id))}
sx={{ ml: 'auto' }} sx={{ ml: 'auto' }}
> >
<PrintIcon /> <PrintIcon />
@ -97,7 +113,7 @@ export function MobileLayout({
sx={{ sx={{
p: 2, p: 2,
cursor: 'pointer', cursor: 'pointer',
bgcolor: selectedGroup?.id === group.id ? 'action.selected' : 'transparent', // no bg color
'&:hover': { bgcolor: 'action.hover' }, '&:hover': { bgcolor: 'action.hover' },
}} }}
onClick={() => onGroupSelect(group)} onClick={() => onGroupSelect(group)}
@ -108,7 +124,7 @@ export function MobileLayout({
</> </>
)} )}
{isTaskView(selectedGroup, selectedTask) && ( {isTaskView(selectedGroup, selectedTask) && isGroupWithTasks(selectedGroup) && (
<> <>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}> <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Tasks</Typography> <Typography variant="h6">Tasks</Typography>
@ -121,11 +137,27 @@ export function MobileLayout({
sx={{ sx={{
p: 2, p: 2,
cursor: 'pointer', cursor: 'pointer',
bgcolor: selectedTask?.id === task.id ? 'action.selected' : 'transparent', // no bg color
'&:hover': { bgcolor: 'action.hover' },
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}} }}
onClick={() => onTaskSelect(task)}
> >
<Typography>{task.name}</Typography> <Box onClick={() => onTaskSelect(task)} sx={{ flex: 1 }}>
<Typography>{task.name}</Typography>
</Box>
{onPrintTask && selectedUser && (
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
onPrintTask(String(task.id), String(selectedUser.id));
}}
>
<PrintIcon />
</IconButton>
)}
</Box> </Box>
))} ))}
</Box> </Box>
@ -146,10 +178,25 @@ export function MobileLayout({
p: 2, p: 2,
cursor: 'pointer', cursor: 'pointer',
bgcolor: selectedStep?.id === step.id ? 'action.selected' : 'transparent', bgcolor: selectedStep?.id === step.id ? 'action.selected' : 'transparent',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}} }}
onClick={() => onStepSelect(step)}
> >
<Typography>{step.name}</Typography> <Box onClick={() => onStepSelect(step)} sx={{ flex: 1 }}>
<Typography>{step.order}: {step.name}</Typography>
</Box>
{onPrintStep && selectedUser && (
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
onPrintStep(String(step.id), String(selectedUser.id));
}}
>
<PrintIcon />
</IconButton>
)}
</Box> </Box>
))} ))}
</Box> </Box>

View File

@ -24,10 +24,11 @@ export const formatUtils = {
* @param startIndex Starting index for numbering (0 for no numbers) * @param startIndex Starting index for numbering (0 for no numbers)
* @returns Array of formatted lines * @returns Array of formatted lines
*/ */
formatList(items: string[], startIndex: number = 0): string[] { formatList(items: string[], startIndex: number = 0, prefix: string = ''): string[] {
return items.map((item, index) => { return items.map((item, index) => {
const num = startIndex > 0 ? `${index + startIndex}: ` : ''; const num = startIndex > 0 ? `${index + startIndex}: ` : '';
return this.formatCheckbox(`${num}${item}`); const body = this.formatCheckbox(`${num}${item}`);
return `${prefix}${body}`;
}); });
}, },

View File

@ -37,15 +37,10 @@ export class TestPrinter implements Printer {
const content = [ const content = [
...formatUtils.formatSection(`Task: ${task.name}`, ''), ...formatUtils.formatSection(`Task: ${task.name}`, ''),
...taskSteps.map((step, index) => ...formatUtils.formatList(taskSteps.map((step, index) =>
formatUtils.formatSection( formatUtils.formatStepHeader(step.name, index + 1, task.name, true)
formatUtils.formatStepHeader(step.name, index + 1, task.name, true), ), 0, '- '),
step.instructions, ].join('\n') + '\n';
'-',
true,
)
).flat(),
].join('\n');
await fs.writeFile(filename, content); await fs.writeFile(filename, content);
logger.info(`Printed task ${task.id} to ${filename}`); logger.info(`Printed task ${task.id} to ${filename}`);
@ -69,7 +64,7 @@ export class TestPrinter implements Printer {
const content = formatUtils.formatSection( const content = formatUtils.formatSection(
formatUtils.formatStepHeader(step.name, stepNumber, task?.name), formatUtils.formatStepHeader(step.name, stepNumber, task?.name),
step.instructions step.instructions
).join('\n'); ).join('\n') + '\n';
await fs.writeFile(filename, content); await fs.writeFile(filename, content);
logger.info(`Printed step ${step.id} to ${filename}`); logger.info(`Printed step ${step.id} to ${filename}`);