diff --git a/server/src/schema/resolvers/recipe.ts b/server/src/schema/resolvers/recipe.ts index 93e62ae..d199694 100644 --- a/server/src/schema/resolvers/recipe.ts +++ b/server/src/schema/resolvers/recipe.ts @@ -2,45 +2,101 @@ import { prisma } from '../../db/client'; import type { Prisma } from '@prisma/client'; import { GraphQLError } from 'graphql'; import type { Context } from '../../auth/session'; +console.log +//ny filtreringssystem +type RecipesArgs = { + search?: string; + category?: string; + tags?: string[]; + maxPrepTime?: number; + sortBy?: 'NEWEST' | 'OLDEST' | 'PREP_TIME_ASC' | 'PREP_TIME_DESC'; + page?: number; + pageSize?: number; +}; + +//all sortering har id som siste ledd, viktig for postgres, ellers kan vi få rader med lik veri i random rekkefølge +const orderByMap: Record< +NonNullable, +Prisma.RecipeOrderByWithRelationInput[] +> = { + NEWEST: [{ createdAt: 'desc' }, { id: 'asc' }], + OLDEST: [{ createdAt: 'asc' }, { id: 'asc' }], + PREP_TIME_ASC: [{ prepTimeMinutes: 'asc' }, { id: 'asc' }], + PREP_TIME_DESC: [{ prepTimeMinutes: 'desc' }, { id: 'asc' }], +}; +//passer på samme skrivemåte overalt +function normalizeTag(name: string) { + const t = name.trim(); + return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase(); +} export const recipeResolvers = { Query: { - recipes: async ( - _: unknown, - args: { search?: string; category?: string; page?: number; pageSize?: number }, - ) => { + recipes: async (_: unknown, args: RecipesArgs) => { const page = Math.max(args.page ?? 1, 1); const pageSize = Math.min(Math.max(args.pageSize ?? 20, 1), 50); const where: Prisma.RecipeWhereInput = { status: 'PUBLISHED' }; - if (args.search) { - where.title = { contains: args.search, mode: 'insensitive' }; + + //søk tittel og ingredisensnavn - har gjort den casaesensitiv for nå + const search = args.search?.trim().slice(0, 100); + if (search) { + where.OR = [ + { title: { contains: search, mode: 'insensitive' } }, + { ingredients: { some: { name: { contains: search, mode: 'insensitive' } } } }, + ]; } if (args.category) { where.category = args.category; } + if (args.maxPrepTime && args.maxPrepTime > 0) { + where.prepTimeMinutes = { lte: args.maxPrepTime }; + } + + // Alle valgte tags må være med (OG) + if (args.tags?.length) { + where.AND = args.tags.map((name) => ({ + tags: { some: { name: { equals: name, mode: 'insensitive' } } }, + })); + } + const [items, totalCount] = await prisma.$transaction([ prisma.recipe.findMany({ where, include: { ingredients: true, steps: true, tags: true }, skip: (page - 1) * pageSize, take: pageSize, - orderBy: { createdAt: 'desc' }, + orderBy: orderByMap[args.sortBy ?? 'NEWEST'], }), prisma.recipe.count({ where }), ]); return { items, totalCount }; }, + recipe: async (_: unknown, args: { id: string }) => { return prisma.recipe.findUnique({ where: { id: args.id }, include: { ingredients: true, steps: true, tags: true }, }); }, + + + tags: () => prisma.tag.findMany({ orderBy: { name: 'asc' } }), + + categories: async () => { + const rows = await prisma.recipe.findMany({ + where: { status: 'PUBLISHED' }, + distinct: ['category'], + select: { category: true }, + orderBy: { category: 'asc' }, + }); + return rows.map((r) => r.category); }, +}, + Mutation: { createRecipe: async (_: unknown, args: { input: any }, context: Context) => { // Bare innloggede brukere og gjestebrukere kan lage oppskrift @@ -49,6 +105,7 @@ export const recipeResolvers = { extensions: { code: 'UNAUTHENTICATED' }, }); } + const { ingredients, steps, tagNames, ...rest } = args.input; return prisma.recipe.create({ data: { @@ -59,7 +116,7 @@ export const recipeResolvers = { steps: { create: steps }, tags: tagNames ? { - connectOrCreate: tagNames.map((name: string) => ({ + connectOrCreate: (tagNames as string[]).map(normalizeTag).map((name) => ({ where: { name }, create: { name }, })), diff --git a/server/src/schema/typeDefs/recipe.graphql b/server/src/schema/typeDefs/recipe.graphql index 316fa01..5df5a2b 100644 --- a/server/src/schema/typeDefs/recipe.graphql +++ b/server/src/schema/typeDefs/recipe.graphql @@ -62,11 +62,30 @@ input CreateRecipeInput { tagNames: [String!] } +enum RecipeSort { + NEWEST + OLDEST + PREP_TIME_ASC + PREP_TIME_DESC +} + type Query { - recipes(search: String, category: String, page: Int, pageSize: Int): RecipeConnection! + recipes( + search: String + category: String + tags: [String!] + maxPrepTime: Int + sortBy: RecipeSort = NEWEST + page: Int, + pageSize: Int + ): RecipeConnection! + + + tags: [Tag!]! + categories: [String!]! recipe(id: ID!): Recipe } type Mutation { createRecipe(input: CreateRecipeInput!): Recipe! -} \ No newline at end of file +}