Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import type { UseFormReturnType } from '@mantine/form';
import type { RefObject } from 'react';
import type {
RecipeIngredient,
RecipeIngredientId,
RecipeMetadataOption,
RecipePreparationStep,
} from '@/types/recipe';
export type MetadataOption = RecipeMetadataOption;
interface FormListItem {
localId: RecipeIngredientId;
}
export interface FormIngredient
extends Omit<RecipeIngredient, 'quantity'>,
FormListItem {
quantity: number | '';
}
export interface FormPreparationStep
extends RecipePreparationStep,
FormListItem {}
export interface RecipeCompletion {
done: number;
total: number;
percent: number;
}
export interface RecipeFormValues {
title: string;
description: string;
imgSrc: string;
servings: number | '';
cookingTime: number | '';
difficultyLevel: MetadataOption | null;
category: MetadataOption | null;
labels: string[]; // array of keys
youtubeLink: string;
ingredients: FormIngredient[];
preparationSteps: FormPreparationStep[];
// New time fields
prepTimeMinutes: number | '';
cookTimeMinutes: number | '';
restTimeMinutes: number | '';
// New metadata fields
servingUnit: MetadataOption | null;
cuisine: MetadataOption | null;
dietaryFlags: string[];
allergens: string[];
equipment: string[];
costLevel: MetadataOption | null;
// Text fields
tips: string;
substitutions: string;
// SEO fields
slug: string;
seoTitle: string;
seoDescription: string;
socialImage: string;
}
export type ComposerMode = 'create' | 'edit';
export type ComposerSection = 'basics' | 'media' | 'ingredients' | 'steps';
export interface UseRecipeFormProps {
metadataLoaded: boolean;
onSectionChange: (section: ComposerSection) => void;
labels: MetadataOption[];
}
export interface DraftState {
updatedAt: number;
values: RecipeFormValues;
}
export interface RecipeComposerProps {
mode: ComposerMode;
form: UseFormReturnType<RecipeFormValues>;
handlePublish: (values: RecipeFormValues) => void;
submitLoading: boolean;
completion: RecipeCompletion;
lastSavedLabel: string;
onSave: () => void;
onReset: () => void;
addIngredient: () => void;
addStep: () => void;
/** Header title (e.g. "Create Recipe" | "Edit Recipe") */
headerTitle: string;
/** Submit button label (e.g. "Publish" | "Save Changes") */
submitLabel: string;
/** Reset button label (e.g. "Clear draft" | "Reset changes") */
resetLabel: string;
/**
* Optional ref that parent components can use to imperatively
* navigate to a specific section (e.g. on validation failure).
*/
goToSectionRef?: RefObject<((section: ComposerSection) => void) | null>;
}
export interface PreviewProps {
labels: MetadataOption[];
values: RecipeFormValues;
}
|