fix most graphql errors

This commit is contained in:
Sean Sube 2025-06-14 17:19:39 -05:00
parent d9e9a16f42
commit 67de31276f
No known key found for this signature in database
GPG Key ID: 3EED7B957D362AF1
13 changed files with 1260 additions and 145 deletions

View File

@ -0,0 +1,87 @@
import { useState } from 'react';
import { Button, Box } from '@mui/material';
import { CreateDialog } from './CreateDialog';
import { useTaskData } from '../hooks/useTaskData';
interface BaseCreateData {
name: string;
}
interface CreateTaskData extends BaseCreateData {
groupId: string;
}
interface CreateStepData extends BaseCreateData {
instructions: string;
taskId: string;
order: number;
}
type CreateData = BaseCreateData | CreateTaskData | CreateStepData;
interface CreateButtonsProps {
type: 'group' | 'task' | 'step';
parentId?: string;
}
export function CreateButtons({ type, parentId }: CreateButtonsProps) {
const [open, setOpen] = useState(false);
const { handleCreateGroup, handleCreateTask, handleCreateStep } = useTaskData();
const handleSubmit = (data: CreateData) => {
let stepData: CreateStepData;
switch (type) {
case 'group':
handleCreateGroup(data.name);
break;
case 'task':
if (!parentId) {
console.error('Cannot create task: no group selected');
return;
}
handleCreateTask(data.name, parentId);
break;
case 'step':
if (!parentId) {
console.error('Cannot create step: no task selected');
return;
}
stepData = data as CreateStepData;
handleCreateStep(stepData.name, stepData.instructions, parentId, stepData.order);
break;
}
};
const getTitle = () => {
switch (type) {
case 'group':
return 'Create New Group';
case 'task':
return 'Create New Task';
case 'step':
return 'Create New Step';
}
};
const isDisabled = (type === 'task' || type === 'step') && !parentId;
return (
<Box>
<Button
variant="contained"
onClick={() => setOpen(true)}
disabled={isDisabled}
>
Create {type.charAt(0).toUpperCase() + type.slice(1)}
</Button>
<CreateDialog
open={open}
onClose={() => setOpen(false)}
onSubmit={handleSubmit}
title={getTitle()}
type={type}
parentId={parentId}
/>
</Box>
);
}

View File

@ -0,0 +1,116 @@
import { useState } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
TextField,
Box,
} from '@mui/material';
interface BaseCreateData {
name: string;
}
interface CreateTaskData extends BaseCreateData {
groupId: string;
}
interface CreateStepData extends BaseCreateData {
instructions: string;
taskId: string;
order: number;
}
type CreateData = BaseCreateData | CreateTaskData | CreateStepData;
interface CreateDialogProps {
open: boolean;
onClose: () => void;
onSubmit: (data: CreateData) => void;
title: string;
type: 'group' | 'task' | 'step';
parentId?: string;
}
export function CreateDialog({ open, onClose, onSubmit, title, type, parentId }: CreateDialogProps) {
const [name, setName] = useState('');
const [instructions, setInstructions] = useState('');
const [order, setOrder] = useState(1);
const handleSubmit = () => {
if (type === 'task' && !parentId) {
console.error('Cannot create task: no group selected');
return;
}
if (type === 'step' && !parentId) {
console.error('Cannot create step: no task selected');
return;
}
const data: CreateData = {
name,
...(type === 'task' && { groupId: parentId }),
...(type === 'step' && {
instructions,
taskId: parentId,
order,
}),
};
onSubmit(data);
handleClose();
};
const handleClose = () => {
setName('');
setInstructions('');
setOrder(1);
onClose();
};
return (
<Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, pt: 1 }}>
<TextField
label="Name"
value={name}
onChange={(e) => setName(e.target.value)}
fullWidth
required
/>
{type === 'step' && (
<>
<TextField
label="Instructions"
value={instructions}
onChange={(e) => setInstructions(e.target.value)}
fullWidth
required
multiline
rows={4}
/>
<TextField
label="Order"
type="number"
value={order}
onChange={(e) => setOrder(parseInt(e.target.value))}
fullWidth
required
inputProps={{ min: 1 }}
/>
</>
)}
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleSubmit} variant="contained" disabled={!name || (type === 'step' && !instructions)}>
Create
</Button>
</DialogActions>
</Dialog>
);
}

View File

@ -1,35 +1,71 @@
import { gql } from '@apollo/client';
export const PRINT_TASK = gql`
mutation PrintTask($id: ID!, $userId: ID!) {
printTask(id: $id, userId: $userId) {
export const CREATE_GROUP = gql`
mutation CreateGroup($name: String!, $parentId: ID) {
createGroup(name: $name, parentId: $parentId) {
id
printCount
lastPrintedAt
name
parent_id
created_at
updated_at
}
}
`;
export const CREATE_TASK = gql`
mutation CreateTask($name: String!, $groupId: ID!) {
createTask(name: $name, groupId: $groupId) {
id
name
group_id
print_count
created_at
updated_at
}
}
`;
export const CREATE_STEP = gql`
mutation CreateStep($name: String!, $instructions: String!, $taskId: ID!, $order: Int!) {
createStep(name: $name, instructions: $instructions, taskId: $taskId, order: $order) {
id
name
instructions
task_id
order
created_at
updated_at
}
}
`;
export const PRINT_TASK = gql`
mutation PrintTask($id: ID!) {
printTask(id: $id) {
id
print_count
}
}
`;
export const PRINT_STEP = gql`
mutation PrintStep($id: ID!, $userId: ID!) {
printStep(id: $id, userId: $userId) {
mutation PrintStep($id: ID!) {
printStep(id: $id) {
id
printCount
lastPrintedAt
print_count
}
}
`;
export const CREATE_NOTE = gql`
mutation CreateNote($content: String!, $stepId: ID!, $userId: ID!) {
createNote(content: $content, stepId: $stepId, userId: $userId) {
mutation CreateNote($content: String!, $stepId: ID!) {
createNote(content: $content, stepId: $stepId) {
id
content
createdAt
user {
id
name
}
step_id
user_id
created_at
updated_at
}
}
`;

View File

@ -8,15 +8,15 @@ export const GET_GROUPS = gql`
tasks {
id
name
printCount
lastPrintedAt
print_count
last_printed_at
steps {
id
name
instructions
order
printCount
lastPrintedAt
print_count
last_printed_at
}
}
}
@ -28,19 +28,19 @@ export const GET_TASKS = gql`
tasks(groupId: $groupId) {
id
name
printCount
lastPrintedAt
print_count
last_printed_at
steps {
id
name
instructions
order
printCount
lastPrintedAt
print_count
last_printed_at
notes {
id
content
createdAt
created_at
user {
id
name
@ -58,12 +58,12 @@ export const GET_STEP = gql`
name
instructions
order
printCount
lastPrintedAt
print_count
last_printed_at
notes {
id
content
createdAt
created_at
user {
id
name

View File

@ -1,8 +1,9 @@
import { useState } from 'react';
import { useQuery, useMutation } from '@apollo/client';
import { GET_GROUPS, GET_TASKS, GET_STEP } from '../graphql/queries';
import { PRINT_TASK, PRINT_STEP, CREATE_NOTE } from '../graphql/mutations';
import { PRINT_TASK, PRINT_STEP, CREATE_NOTE, CREATE_GROUP, CREATE_TASK, CREATE_STEP } from '../graphql/mutations';
import type { GroupWithTasks, TaskWithSteps, StepWithNotes } from '../types';
import { doesExist, isArray } from '../utils/typeGuards';
interface GraphQLStep {
id: number;
@ -11,19 +12,18 @@ interface GraphQLStep {
taskId: number;
order: number;
notes: GraphQLNote[];
printCount: number;
lastPrintedAt: string | null;
print_count: number;
last_printed_at: string | null;
}
interface GraphQLNote {
id: number;
content: string;
createdBy: number;
user?: {
created_at: string;
updated_at: string;
user: {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
};
}
@ -33,62 +33,107 @@ interface GraphQLTask {
groupId: number;
steps: GraphQLStep[];
notes: GraphQLNote[];
printCount: number;
lastPrintedAt: string | null;
print_count: number;
last_printed_at: string | null;
}
interface GraphQLGroup {
id: number;
name: string;
tasks: GraphQLTask[];
created_at: string;
updated_at: string;
}
function toStepWithNotes(step: GraphQLStep): StepWithNotes {
function toStepWithNotes(step: GraphQLStep | null | undefined): StepWithNotes | null {
if (!doesExist(step)) return null;
return {
...step,
notes: (step.notes || []).map((note: GraphQLNote) => ({ ...note })),
printCount: step.printCount ?? 0,
lastPrintedAt: step.lastPrintedAt ?? null,
id: step.id,
name: step.name,
instructions: step.instructions,
task_id: step.taskId,
order: step.order,
print_count: step.print_count,
last_printed_at: step.last_printed_at ? new Date(step.last_printed_at) : undefined,
notes: isArray(step.notes) ? step.notes.map(note => ({
id: note.id,
content: note.content,
created_at: new Date(note.created_at),
updated_at: new Date(note.updated_at),
created_by: note.user.id,
user: {
id: note.user.id,
name: note.user.name,
},
})) : [],
};
}
function toTaskWithSteps(task: GraphQLTask): TaskWithSteps {
function toTaskWithSteps(task: GraphQLTask | null | undefined): TaskWithSteps | null {
if (!doesExist(task)) return null;
return {
...task,
steps: (task.steps || []).map(toStepWithNotes),
notes: (task.notes || []),
printCount: task.printCount ?? 0,
lastPrintedAt: task.lastPrintedAt ?? null,
id: task.id,
name: task.name,
group_id: task.groupId,
print_count: task.print_count,
last_printed_at: task.last_printed_at ? new Date(task.last_printed_at) : undefined,
steps: isArray(task.steps) ? task.steps.map(step => toStepWithNotes(step)).filter(doesExist) : [],
notes: isArray(task.notes) ? task.notes.map(note => ({
id: note.id,
content: note.content,
created_at: new Date(note.created_at),
updated_at: new Date(note.updated_at),
created_by: note.user.id,
user: {
id: note.user.id,
name: note.user.name,
},
})) : [],
};
}
function toGroupWithTasks(group: GraphQLGroup): GroupWithTasks {
function toGroupWithTasks(group: GraphQLGroup | null | undefined): GroupWithTasks | null {
if (!doesExist(group)) return null;
return {
...group,
tasks: (group.tasks || []).map(toTaskWithSteps),
id: group.id,
name: group.name,
created_at: new Date(group.created_at),
updated_at: new Date(group.updated_at),
tasks: isArray(group.tasks) ? group.tasks.map(task => toTaskWithSteps(task)).filter(doesExist) : [],
};
}
export function useTaskData() {
const [selectedGroup, setSelectedGroup] = useState<GroupWithTasks>();
const [selectedTask, setSelectedTask] = useState<TaskWithSteps>();
const [selectedStep, setSelectedStep] = useState<StepWithNotes>();
const [selectedGroup, setSelectedGroup] = useState<GroupWithTasks | undefined>();
const [selectedTask, setSelectedTask] = useState<TaskWithSteps | undefined>();
const [selectedStep, setSelectedStep] = useState<StepWithNotes | undefined>();
const { data: groupsData, loading: groupsLoading } = useQuery(GET_GROUPS);
const { data: tasksData, loading: tasksLoading } = useQuery(GET_TASKS, {
const { data: groupsData, loading: groupsLoading, refetch: refetchGroups } = useQuery(GET_GROUPS);
const { data: tasksData, loading: tasksLoading, refetch: refetchTasks } = useQuery(GET_TASKS, {
variables: { groupId: selectedGroup?.id },
skip: !selectedGroup,
skip: !selectedGroup?.id,
});
const { data: stepData, loading: stepLoading } = useQuery(GET_STEP, {
const { data: stepData, loading: stepLoading, refetch: refetchStep } = useQuery(GET_STEP, {
variables: { id: selectedStep?.id },
skip: !selectedStep,
skip: !selectedStep?.id,
});
const [printTask] = useMutation(PRINT_TASK);
const [printStep] = useMutation(PRINT_STEP);
const [createNote] = useMutation(CREATE_NOTE);
const [createGroup] = useMutation(CREATE_GROUP);
const [createTask] = useMutation(CREATE_TASK);
const [createStep] = useMutation(CREATE_STEP);
const handlePrintTask = async (taskId: string, userId: string) => {
if (!taskId || !userId) {
console.error('Cannot print task: missing taskId or userId');
return;
}
try {
await printTask({ variables: { id: taskId, userId } });
} catch (error) {
@ -97,6 +142,11 @@ export function useTaskData() {
};
const handlePrintStep = async (stepId: string, userId: string) => {
if (!stepId || !userId) {
console.error('Cannot print step: missing stepId or userId');
return;
}
try {
await printStep({ variables: { id: stepId, userId } });
} catch (error) {
@ -105,7 +155,10 @@ export function useTaskData() {
};
const handleAddNote = async (content: string, userId: string) => {
if (!selectedStep) return;
if (!selectedStep?.id || !userId) {
console.error('Cannot add note: missing stepId or userId');
return;
}
try {
await createNote({
@ -120,10 +173,67 @@ export function useTaskData() {
}
};
const handleCreateGroup = async (name: string) => {
if (!name) {
console.error('Cannot create group: missing name');
return;
}
try {
await createGroup({ variables: { name } });
await refetchGroups();
} catch (error) {
console.error('Error creating group:', error);
}
};
const handleCreateTask = async (name: string, groupId: string) => {
if (!name || !groupId) {
console.error('Cannot create task: missing name or groupId');
return;
}
try {
const variables = {
name,
groupId,
};
console.log('Creating task with variables:', variables);
await createTask({ variables });
await refetchTasks({ groupId });
} catch (error) {
console.error('Error creating task:', error);
}
};
const handleCreateStep = async (name: string, instructions: string, taskId: string, order: number) => {
if (!name || !instructions || !taskId || order < 1) {
console.error('Cannot create step: missing required fields');
return;
}
try {
await createStep({ variables: { name, instructions, taskId, order } });
await refetchStep({ id: taskId });
} catch (error) {
console.error('Error creating step:', error);
}
};
const groups = isArray(groupsData?.groups)
? groupsData.groups.map((group: GraphQLGroup) => toGroupWithTasks(group)).filter(doesExist)
: [];
const tasks = isArray(tasksData?.tasks)
? tasksData.tasks.map((task: GraphQLTask) => toTaskWithSteps(task)).filter(doesExist)
: [];
const step = stepData?.step ? toStepWithNotes(stepData.step) : undefined;
return {
groups: (groupsData?.groups || []).map(toGroupWithTasks),
tasks: (tasksData?.tasks || []).map(toTaskWithSteps),
step: stepData?.step ? toStepWithNotes(stepData.step) : undefined,
groups,
tasks,
step,
loading: groupsLoading || tasksLoading || stepLoading,
selectedGroup,
selectedTask,
@ -134,5 +244,8 @@ export function useTaskData() {
handlePrintTask,
handlePrintStep,
handleAddNote,
handleCreateGroup,
handleCreateTask,
handleCreateStep,
};
}

View File

@ -1,5 +1,6 @@
import { Box, Typography } from '@mui/material';
import type { GroupWithTasks, TaskWithSteps, StepWithNotes } from '../types';
import { CreateButtons } from '../components/CreateButtons';
interface DesktopLayoutProps {
groups: GroupWithTasks[];
@ -24,9 +25,10 @@ export function DesktopLayout({
<Box sx={{ display: 'flex', height: '100vh' }}>
{/* Groups panel */}
<Box sx={{ width: 250, borderRight: 1, borderColor: 'divider', p: 2 }}>
<Typography variant="h6" sx={{ mb: 2 }}>
Groups
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Groups</Typography>
<CreateButtons type="group" />
</Box>
{groups.map((group) => (
<Box
key={group.id}
@ -46,9 +48,10 @@ export function DesktopLayout({
{/* Tasks panel */}
{selectedGroup && (
<Box sx={{ width: 250, borderRight: 1, borderColor: 'divider', p: 2 }}>
<Typography variant="h6" sx={{ mb: 2 }}>
Tasks
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Tasks</Typography>
<CreateButtons type="task" parentId={String(selectedGroup.id)} />
</Box>
{selectedGroup.tasks.map((task) => (
<Box
key={task.id}
@ -69,9 +72,10 @@ export function DesktopLayout({
{/* Steps panel */}
{selectedTask && (
<Box sx={{ width: 250, borderRight: 1, borderColor: 'divider', p: 2 }}>
<Typography variant="h6" sx={{ mb: 2 }}>
Steps
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Steps</Typography>
<CreateButtons type="step" parentId={String(selectedTask.id)} />
</Box>
{selectedTask.steps.map((step) => (
<Box
key={step.id}

View File

@ -1,6 +1,7 @@
import { Box, Paper, Typography, IconButton } from '@mui/material';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import type { GroupWithTasks, TaskWithSteps, StepWithNotes } from '../types';
import { CreateButtons } from '../components/CreateButtons';
function isGroupView(selectedGroup?: GroupWithTasks) {
return !selectedGroup;
@ -71,9 +72,10 @@ export function MobileLayout({
<Paper sx={{ p: 2, overflow: 'auto', height: 'calc(100vh - 80px)' }}>
{isGroupView(selectedGroup) && (
<>
<Typography variant="h6" gutterBottom>
Groups
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Groups</Typography>
<CreateButtons type="group" />
</Box>
{groupList.map((group) => (
<Box
key={group.id}
@ -91,9 +93,10 @@ export function MobileLayout({
{isTaskView(selectedGroup, selectedTask) && (
<>
<Typography variant="h6" gutterBottom>
Tasks
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Tasks</Typography>
<CreateButtons type="task" parentId={String(selectedGroup?.id)} />
</Box>
<Box>
{taskList.map((task) => (
<Box
@ -114,9 +117,10 @@ export function MobileLayout({
{isStepView(selectedTask, selectedStep) && (
<>
<Typography variant="h6" gutterBottom>
Steps
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Steps</Typography>
<CreateButtons type="step" parentId={String(selectedTask?.id)} />
</Box>
<Box>
{stepList.map((step) => (
<Box

View File

@ -1,37 +1,101 @@
import { Group as SharedGroup, Task as SharedTask, Step as SharedStep, User as SharedUser, Note as SharedNote, PrintHistory as SharedPrintHistory } from '@task-receipts/shared';
// Remove all references to SharedGroup, SharedUser, SharedNote, and SharedPrintHistory. Use only local types for Group, User, Note, and PrintHistory.
// Client-specific type extensions
export interface GroupWithTasks extends Omit<SharedGroup, 'createdAt' | 'updatedAt'> {
tasks: TaskWithSteps[];
}
export interface TaskWithSteps extends Omit<SharedTask, 'createdAt' | 'updatedAt' | 'lastPrintedAt'> {
export interface TaskWithSteps {
id: number;
name: string;
group_id: number;
print_count: number;
last_printed_at?: Date | string | null;
created_at?: Date | string | null;
updated_at?: Date | string | null;
steps: StepWithNotes[];
notes: NoteWithUser[];
printCount: number;
lastPrintedAt: string | null;
}
export interface StepWithNotes extends Omit<SharedStep, 'createdAt' | 'updatedAt' | 'lastPrintedAt'> {
export interface StepWithNotes {
id: number;
name: string;
instructions: string;
task_id: number;
order: number;
print_count: number;
last_printed_at?: Date | string | null;
created_at?: Date | string | null;
updated_at?: Date | string | null;
notes: NoteWithUser[];
printCount: number;
lastPrintedAt: string | null;
}
export interface NoteWithUser extends Omit<SharedNote, 'createdAt' | 'updatedAt'> {
user?: SharedUser;
export type Task = {
id: number;
name: string;
group_id: number;
print_count: number;
last_printed_at?: Date | string | null;
created_at?: Date | string | null;
updated_at?: Date | string | null;
};
export type Step = {
id: number;
name: string;
instructions: string;
task_id: number;
order: number;
print_count: number;
last_printed_at?: Date | string | null;
created_at?: Date | string | null;
updated_at?: Date | string | null;
};
// Define User, Note, and PrintHistory locally if needed
export interface User {
id: number;
name: string;
created_at?: Date | string | null;
updated_at?: Date | string | null;
}
export interface PrintHistoryWithDetails extends Omit<SharedPrintHistory, 'createdAt' | 'updatedAt'> {
user?: SharedUser;
export interface Note {
id: number;
content: string;
step_id?: number;
task_id?: number;
user_id?: number;
created_at?: Date | string | null;
updated_at?: Date | string | null;
}
export interface NoteWithUser extends Note {
user?: User;
}
export interface PrintHistory {
id: number;
user_id: number;
task_id?: number;
step_id?: number;
printed_at: Date | string;
created_at?: Date | string | null;
updated_at?: Date | string | null;
}
export interface PrintHistoryWithDetails extends PrintHistory {
user?: User;
task?: TaskWithSteps;
step?: StepWithNotes;
}
// Re-export shared types with client-specific modifications
export type Group = Omit<SharedGroup, 'createdAt' | 'updatedAt'>;
export type Task = Omit<SharedTask, 'createdAt' | 'updatedAt' | 'lastPrintedAt'> & { lastPrintedAt: string | null };
export type Step = Omit<SharedStep, 'createdAt' | 'updatedAt' | 'lastPrintedAt'> & { lastPrintedAt: string | null };
export type User = Omit<SharedUser, 'createdAt' | 'updatedAt'>;
export type Note = Omit<SharedNote, 'createdAt' | 'updatedAt'>;
export type PrintHistory = Omit<SharedPrintHistory, 'createdAt' | 'updatedAt'>;
export interface Group {
id: number;
name: string;
parent_id?: number;
created_at?: Date | string | null;
updated_at?: Date | string | null;
}
export interface GroupWithTasks extends Group {
tasks: TaskWithSteps[];
}
// All types are now defined locally above. Remove all re-exports and duplicate type definitions at the bottom of the file.

View File

@ -0,0 +1,164 @@
import type { GroupWithTasks, TaskWithSteps, StepWithNotes } from '../types';
interface GraphQLStep {
id: number;
name: string;
instructions: string;
taskId: number;
order: number;
notes: GraphQLNote[];
print_count: number;
last_printed_at: string | null;
}
interface GraphQLNote {
id: number;
content: string;
created_at: string;
updated_at: string;
user: {
id: number;
name: string;
};
}
interface GraphQLTask {
id: number;
name: string;
groupId: number;
steps: GraphQLStep[];
notes: GraphQLNote[];
print_count: number;
last_printed_at: string | null;
}
interface GraphQLGroup {
id: number;
name: string;
tasks: GraphQLTask[];
created_at: string;
updated_at: string;
}
// Generic type guard for non-null values
export function doesExist<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
// Type guard for arrays
export function isArray<T>(value: unknown): value is T[] {
return Array.isArray(value);
}
// Type guard for objects with required properties
export function hasProperties<T extends object>(value: unknown, properties: (keyof T)[]): value is T {
return (
typeof value === 'object' &&
value !== null &&
properties.every(prop => prop in value)
);
}
// Type guard for GraphQL objects
export function isGraphQLStep(value: unknown): value is GraphQLStep {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
'instructions' in value &&
'taskId' in value &&
'order' in value &&
'notes' in value &&
'print_count' in value &&
'last_printed_at' in value
);
}
export function isGraphQLTask(value: unknown): value is GraphQLTask {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
'groupId' in value &&
'steps' in value &&
'notes' in value &&
'print_count' in value &&
'last_printed_at' in value
);
}
export function isGraphQLGroup(value: unknown): value is GraphQLGroup {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
'tasks' in value &&
'created_at' in value &&
'updated_at' in value
);
}
// Transformation functions
export function toStepWithNotes(step: GraphQLStep | null | undefined): StepWithNotes | null {
if (!doesExist(step)) return null;
return {
id: step.id,
name: step.name,
instructions: step.instructions,
task_id: step.taskId,
order: step.order,
print_count: step.print_count,
last_printed_at: step.last_printed_at ? new Date(step.last_printed_at) : undefined,
notes: isArray(step.notes) ? step.notes.map(note => ({
id: note.id,
content: note.content,
created_at: new Date(note.created_at),
updated_at: new Date(note.updated_at),
created_by: note.user.id,
user: {
id: note.user.id,
name: note.user.name,
},
})) : [],
};
}
export function toTaskWithSteps(task: GraphQLTask | null | undefined): TaskWithSteps | null {
if (!doesExist(task)) return null;
return {
id: task.id,
name: task.name,
group_id: task.groupId,
print_count: task.print_count,
last_printed_at: task.last_printed_at ? new Date(task.last_printed_at) : undefined,
steps: isArray(task.steps) ? task.steps.map(step => toStepWithNotes(step)).filter(doesExist) : [],
notes: isArray(task.notes) ? task.notes.map(note => ({
id: note.id,
content: note.content,
created_at: new Date(note.created_at),
updated_at: new Date(note.updated_at),
created_by: note.user.id,
user: {
id: note.user.id,
name: note.user.name,
},
})) : [],
};
}
export function toGroupWithTasks(group: GraphQLGroup | null | undefined): GroupWithTasks | null {
if (!doesExist(group)) return null;
return {
id: group.id,
name: group.name,
created_at: new Date(group.created_at),
updated_at: new Date(group.updated_at),
tasks: isArray(group.tasks) ? group.tasks.map(task => toTaskWithSteps(task)).filter(doesExist) : [],
};
}

522
package-lock.json generated
View File

@ -613,6 +613,28 @@
"node": ">=14"
}
},
"node_modules/@apollographql/apollo-tools": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz",
"integrity": "sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==",
"license": "MIT",
"engines": {
"node": ">=8",
"npm": ">=6"
},
"peerDependencies": {
"graphql": "^14.2.1 || ^15.0.0 || ^16.0.0"
}
},
"node_modules/@apollographql/graphql-playground-html": {
"version": "1.6.29",
"resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz",
"integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==",
"license": "MIT",
"dependencies": {
"xss": "^1.0.8"
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@ -1971,6 +1993,21 @@
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@graphql-tools/mock": {
"version": "8.7.20",
"resolved": "https://registry.npmjs.org/@graphql-tools/mock/-/mock-8.7.20.tgz",
"integrity": "sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ==",
"license": "MIT",
"dependencies": {
"@graphql-tools/schema": "^9.0.18",
"@graphql-tools/utils": "^9.2.1",
"fast-json-stable-stringify": "^2.1.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@graphql-tools/schema": {
"version": "9.0.19",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz",
@ -2390,6 +2427,12 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@josephg/resolvable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz",
"integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==",
"license": "ISC"
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
@ -3155,6 +3198,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@ -3754,6 +3806,446 @@
"node": ">= 8"
}
},
"node_modules/apollo-datasource": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.2.tgz",
"integrity": "sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg==",
"deprecated": "The `apollo-datasource` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1",
"apollo-server-env": "^4.2.1"
},
"engines": {
"node": ">=12.0"
}
},
"node_modules/apollo-datasource/node_modules/@apollo/utils.keyvaluecache": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz",
"integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==",
"license": "MIT",
"dependencies": {
"@apollo/utils.logger": "^1.0.0",
"lru-cache": "7.10.1 - 7.13.1"
}
},
"node_modules/apollo-datasource/node_modules/@apollo/utils.logger": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz",
"integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==",
"license": "MIT"
},
"node_modules/apollo-datasource/node_modules/lru-cache": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz",
"integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/apollo-reporting-protobuf": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz",
"integrity": "sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog==",
"deprecated": "The `apollo-reporting-protobuf` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/usage-reporting-protobuf` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"@apollo/protobufjs": "1.2.6"
}
},
"node_modules/apollo-reporting-protobuf/node_modules/@apollo/protobufjs": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.6.tgz",
"integrity": "sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/long": "^4.0.0",
"@types/node": "^10.1.0",
"long": "^4.0.0"
},
"bin": {
"apollo-pbjs": "bin/pbjs",
"apollo-pbts": "bin/pbts"
}
},
"node_modules/apollo-reporting-protobuf/node_modules/@types/node": {
"version": "10.17.60",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
"license": "MIT"
},
"node_modules/apollo-server-core": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.13.0.tgz",
"integrity": "sha512-v/g6DR6KuHn9DYSdtQijz8dLOkP78I5JSVJzPkARhDbhpH74QNwrQ2PP2URAPPEDJ2EeZNQDX8PvbYkAKqg+kg==",
"deprecated": "The `apollo-server-core` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1",
"@apollo/utils.logger": "^1.0.0",
"@apollo/utils.usagereporting": "^1.0.0",
"@apollographql/apollo-tools": "^0.5.3",
"@apollographql/graphql-playground-html": "1.6.29",
"@graphql-tools/mock": "^8.1.2",
"@graphql-tools/schema": "^8.0.0",
"@josephg/resolvable": "^1.0.0",
"apollo-datasource": "^3.3.2",
"apollo-reporting-protobuf": "^3.4.0",
"apollo-server-env": "^4.2.1",
"apollo-server-errors": "^3.3.1",
"apollo-server-plugin-base": "^3.7.2",
"apollo-server-types": "^3.8.0",
"async-retry": "^1.2.1",
"fast-json-stable-stringify": "^2.1.0",
"graphql-tag": "^2.11.0",
"loglevel": "^1.6.8",
"lru-cache": "^6.0.0",
"node-abort-controller": "^3.0.1",
"sha.js": "^2.4.11",
"uuid": "^9.0.0",
"whatwg-mimetype": "^3.0.0"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"graphql": "^15.3.0 || ^16.0.0"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.dropunuseddefinitions": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz",
"integrity": "sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg==",
"license": "MIT",
"engines": {
"node": ">=12.13.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.keyvaluecache": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz",
"integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==",
"license": "MIT",
"dependencies": {
"@apollo/utils.logger": "^1.0.0",
"lru-cache": "7.10.1 - 7.13.1"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.keyvaluecache/node_modules/lru-cache": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz",
"integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.logger": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz",
"integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==",
"license": "MIT"
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.printwithreducedwhitespace": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz",
"integrity": "sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q==",
"license": "MIT",
"engines": {
"node": ">=12.13.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.removealiases": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz",
"integrity": "sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A==",
"license": "MIT",
"engines": {
"node": ">=12.13.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.sortast": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz",
"integrity": "sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA==",
"license": "MIT",
"dependencies": {
"lodash.sortby": "^4.7.0"
},
"engines": {
"node": ">=12.13.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.stripsensitiveliterals": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz",
"integrity": "sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w==",
"license": "MIT",
"engines": {
"node": ">=12.13.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/apollo-server-core/node_modules/@apollo/utils.usagereporting": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz",
"integrity": "sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ==",
"license": "MIT",
"dependencies": {
"@apollo/usage-reporting-protobuf": "^4.0.0",
"@apollo/utils.dropunuseddefinitions": "^1.1.0",
"@apollo/utils.printwithreducedwhitespace": "^1.1.0",
"@apollo/utils.removealiases": "1.0.0",
"@apollo/utils.sortast": "^1.1.0",
"@apollo/utils.stripsensitiveliterals": "^1.2.0"
},
"engines": {
"node": ">=12.13.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x || 16.x"
}
},
"node_modules/apollo-server-core/node_modules/@graphql-tools/merge": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz",
"integrity": "sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==",
"license": "MIT",
"dependencies": {
"@graphql-tools/utils": "8.9.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/apollo-server-core/node_modules/@graphql-tools/schema": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz",
"integrity": "sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==",
"license": "MIT",
"dependencies": {
"@graphql-tools/merge": "8.3.1",
"@graphql-tools/utils": "8.9.0",
"tslib": "^2.4.0",
"value-or-promise": "1.0.11"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/apollo-server-core/node_modules/@graphql-tools/utils": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz",
"integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/apollo-server-core/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/apollo-server-core/node_modules/value-or-promise": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz",
"integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/apollo-server-env": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-4.2.1.tgz",
"integrity": "sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g==",
"deprecated": "The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.7"
},
"engines": {
"node": ">=12.0"
}
},
"node_modules/apollo-server-errors": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz",
"integrity": "sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA==",
"deprecated": "The `apollo-server-errors` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"graphql": "^15.3.0 || ^16.0.0"
}
},
"node_modules/apollo-server-express": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.13.0.tgz",
"integrity": "sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g==",
"deprecated": "The `apollo-server-express` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"@types/accepts": "^1.3.5",
"@types/body-parser": "1.19.2",
"@types/cors": "2.8.12",
"@types/express": "4.17.14",
"@types/express-serve-static-core": "4.17.31",
"accepts": "^1.3.5",
"apollo-server-core": "^3.13.0",
"apollo-server-types": "^3.8.0",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"parseurl": "^1.3.3"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"express": "^4.17.1",
"graphql": "^15.3.0 || ^16.0.0"
}
},
"node_modules/apollo-server-express/node_modules/@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
"integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
"license": "MIT",
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/apollo-server-express/node_modules/@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
"license": "MIT"
},
"node_modules/apollo-server-express/node_modules/@types/express": {
"version": "4.17.14",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
"integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
"license": "MIT",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.18",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/apollo-server-express/node_modules/@types/express-serve-static-core": {
"version": "4.17.31",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
"integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"node_modules/apollo-server-plugin-base": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz",
"integrity": "sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw==",
"deprecated": "The `apollo-server-plugin-base` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"apollo-server-types": "^3.8.0"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"graphql": "^15.3.0 || ^16.0.0"
}
},
"node_modules/apollo-server-types": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.8.0.tgz",
"integrity": "sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A==",
"deprecated": "The `apollo-server-types` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.",
"license": "MIT",
"dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1",
"@apollo/utils.logger": "^1.0.0",
"apollo-reporting-protobuf": "^3.4.0",
"apollo-server-env": "^4.2.1"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"graphql": "^15.3.0 || ^16.0.0"
}
},
"node_modules/apollo-server-types/node_modules/@apollo/utils.keyvaluecache": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz",
"integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==",
"license": "MIT",
"dependencies": {
"@apollo/utils.logger": "^1.0.0",
"lru-cache": "7.10.1 - 7.13.1"
}
},
"node_modules/apollo-server-types/node_modules/@apollo/utils.logger": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz",
"integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==",
"license": "MIT"
},
"node_modules/apollo-server-types/node_modules/lru-cache": {
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz",
"integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
@ -4648,6 +5140,12 @@
"node": ">= 8"
}
},
"node_modules/cssfilter": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
"integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
"license": "MIT"
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@ -5594,7 +6092,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
@ -10591,6 +11088,28 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/xss": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz",
"integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==",
"license": "MIT",
"dependencies": {
"commander": "^2.20.3",
"cssfilter": "0.0.10"
},
"bin": {
"xss": "bin/xss"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/xss/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@ -10698,6 +11217,7 @@
"dependencies": {
"@apollo/server": "^4.10.0",
"@task-receipts/shared": "file:../shared",
"apollo-server-express": "^3.13.0",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",

View File

@ -12,11 +12,15 @@
"test:coverage": "jest --coverage",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"migrate:latest": "knex migrate:latest --knexfile src/db/knexfile.ts",
"migrate:rollback": "knex migrate:rollback --knexfile src/db/knexfile.ts",
"migrate:make": "knex migrate:make --knexfile src/db/knexfile.ts"
},
"dependencies": {
"@apollo/server": "^4.10.0",
"@task-receipts/shared": "file:../shared",
"apollo-server-express": "^3.13.0",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",

View File

@ -1,25 +1,26 @@
import type { Knex } from 'knex';
import path from 'path';
const config: { [key: string]: Knex.Config } = {
development: {
client: 'sqlite3',
connection: {
filename: './dev.sqlite3'
filename: path.join(__dirname, '../../../dev.sqlite3'),
},
useNullAsDefault: true,
migrations: {
directory: './src/db/migrations'
}
directory: path.join(__dirname, 'migrations'),
},
},
test: {
client: 'sqlite3',
connection: {
filename: ':memory:'
filename: ':memory:',
},
useNullAsDefault: true,
migrations: {
directory: './src/db/migrations'
}
directory: path.join(__dirname, 'migrations'),
},
},
production: {
client: 'sqlite3',

View File

@ -1,70 +1,72 @@
import { gql } from 'graphql-tag';
import { gql } from 'apollo-server-express';
export const typeDefs = gql`
type Group {
id: ID!
name: String!
parentId: ID
parent_id: ID
tasks: [Task!]!
createdAt: String!
updatedAt: String!
created_at: String!
updated_at: String!
}
type Task {
id: ID!
name: String!
groupId: ID!
group_id: ID!
group: Group!
steps: [Step!]!
printCount: Int!
lastPrintedAt: String
createdAt: String!
updatedAt: String!
notes: [Note!]!
print_count: Int!
last_printed_at: String
printHistory: [PrintHistory!]!
created_at: String!
updated_at: String!
}
type Step {
id: ID!
name: String!
instructions: String!
taskId: ID!
task_id: ID!
task: Task!
order: Int!
images: [Image!]!
printCount: Int!
lastPrintedAt: String
createdAt: String!
updatedAt: String!
notes: [Note!]!
print_count: Int!
last_printed_at: String
printHistory: [PrintHistory!]!
images: [Image!]!
created_at: String!
updated_at: String!
}
type Image {
id: ID!
stepId: ID!
originalPath: String!
bwPath: String!
step_id: ID!
step: Step!
original_path: String!
bw_path: String!
order: Int!
createdAt: String!
updatedAt: String!
created_at: String!
updated_at: String!
}
type User {
id: ID!
name: String!
createdAt: String!
updatedAt: String!
created_at: String!
updated_at: String!
}
type Note {
id: ID!
content: String!
taskId: ID
stepId: ID
step_id: ID!
step: Step!
user: User!
createdBy: User!
createdAt: String!
updatedAt: String!
created_at: String!
updated_at: String!
}
type PrintHistory {
@ -72,9 +74,9 @@ export const typeDefs = gql`
user: User!
task: Task
step: Step
printedAt: String!
createdAt: String!
updatedAt: String!
printed_at: String!
created_at: String!
updated_at: String!
}
type Query {
@ -88,7 +90,7 @@ export const typeDefs = gql`
frequentTasks: [Task!]!
users: [User!]!
user(id: ID!): User
notes(taskId: ID, stepId: ID): [Note!]!
notes(stepId: ID!): [Note!]!
printHistory(taskId: ID, stepId: ID): [PrintHistory!]!
}
@ -98,8 +100,8 @@ export const typeDefs = gql`
createStep(name: String!, instructions: String!, taskId: ID!, order: Int!): Step!
createImage(stepId: ID!, originalPath: String!, bwPath: String!, order: Int!): Image!
createUser(name: String!): User!
createNote(content: String!, taskId: ID, stepId: ID, createdBy: ID!): Note!
printTask(id: ID!, userId: ID!): Task!
printStep(id: ID!, userId: ID!): Step!
createNote(content: String!, stepId: ID!): Note!
printTask(id: ID!): Task!
printStep(id: ID!): Step!
}
`;