Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions client/src/components/profile/DeleteRecipesDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { useState } from 'react';
import * as RadixDialog from '@radix-ui/react-dialog';
import { useMutation } from '@apollo/client/react';
import { Button } from '../ui/Button';
import { DELETE_MY_RECIPES, MY_RECIPES } from '../../graphql/profile';
import {
DELETE_MY_RECIPES,
MY_RECIPES,
MY_DRAFTS,
} from '../../graphql/profile';

type DeleteRecipesDialogProps = {
recipes: { id: string; title: string }[];
Expand All @@ -16,7 +20,7 @@ export function DeleteRecipesDialog({
}: DeleteRecipesDialogProps) {
const [open, setOpen] = useState(false);
const [deleteRecipes, { loading, error }] = useMutation(DELETE_MY_RECIPES, {
refetchQueries: [MY_RECIPES],
refetchQueries: [MY_RECIPES, MY_DRAFTS],
});

const count = recipes.length;
Expand Down
13 changes: 13 additions & 0 deletions client/src/graphql/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ export const MY_RECIPES: TypedDocumentNode<{ myRecipes: Recipe[] }> = gql`
}
`;

// utkastene innlogget bruker har lagret
export const MY_DRAFTS: TypedDocumentNode<{ myDrafts: Recipe[] }> = gql`
query MyDrafts {
myDrafts {
id
imageUrl
title
prepTimeMinutes
servings
}
}
`;

// sletter oppskrifter brukeren selv har laget
export const DELETE_MY_RECIPES: TypedDocumentNode<
{ deleteMyRecipes: number },
Expand Down
6 changes: 2 additions & 4 deletions client/src/routes/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ export default function LoginPage() {
const { continueAsGuest } = useCurrentUser();

// Midlertidig. token skal sendes til backend senere
function handleGoogleSuccess(response: CredentialResponse) {
console.log('Google-token:', response.credential);
}
function handleGoogleSuccess(_response: CredentialResponse) {}

// logger inn som gjest og går til forside
async function handleGuest() {
Expand Down Expand Up @@ -57,7 +55,7 @@ export default function LoginPage() {
<div ref={googleBoxRef} className="flex justify-center">
<GoogleLogin
onSuccess={handleGoogleSuccess}
onError={() => console.log('Google innlogging feilet')}
onError={() => {}}
text="signin_with"
size="large"
width={googleButtonWidth}
Expand Down
49 changes: 26 additions & 23 deletions client/src/routes/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,31 @@ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked';
import { useCurrentUser } from '../lib/useCurrentUser';
import { useQuery } from '@apollo/client/react';
import { MY_RECIPES } from '../graphql/profile';
import { MY_RECIPES, MY_DRAFTS } from '../graphql/profile';
import { RecipeCard } from '../components/recipe/RecipeCard';
import { MY_FAVORITES } from '../graphql/favorites';
import { AnimatePresence, motion } from 'motion/react';
import { fadeUp, quick, glide } from '../lib/motion';

type Tab = 'preferences' | 'favorites' | 'shared' | 'sharedWithMe';
type Tab = 'preferences' | 'shared' | 'drafts' | 'favorites';
type RecipeTab = Exclude<Tab, 'preferences'>;

// tekstene for hver oppskriftsfane (tomme til backend er koblet på)
// tekstene for hver oppskriftsfane
const RECIPE_TABS: Record<RecipeTab, { label: string; empty: string }> = {
favorites: { label: 'Favoritter', empty: 'Du har ingen favoritter ennå.' },
shared: {
label: 'Mine oppskrifter',
empty: 'Du har ikke laget noen oppskrifter ennå.',
},
sharedWithMe: {
label: 'Delt med meg',
empty: 'Ingen har delt oppskrifter med deg ennå.',
},
drafts: { label: 'Utkast', empty: 'Du har ingen utkast.' },
favorites: { label: 'Favoritter', empty: 'Du har ingen favoritter ennå.' },
};

// pillene i mobilmenyen
const MOBILE_TABS: { value: Tab; label: string }[] = [
{ value: 'preferences', label: 'Preferanser' },
{ value: 'favorites', label: 'Favoritter' },
{ value: 'shared', label: 'Mine oppskrifter' },
{ value: 'sharedWithMe', label: 'Delt med meg' },
{ value: 'drafts', label: 'Utkast' },
{ value: 'favorites', label: 'Favoritter' },
];

// rosa fokusring
Expand All @@ -59,32 +56,39 @@ export default function ProfilePage() {
const { data: favoritesData } = useQuery(MY_FAVORITES, {
fetchPolicy: 'cache-and-network',
});
// utkastene til brukeren
const { data: draftsData } = useQuery(MY_DRAFTS, {
fetchPolicy: 'cache-and-network',
});

// RequireAuth sørger for at user finnes
if (!user) return null;

const isGuest = user.authProvider === 'GUEST';
const myRecipes = myRecipesData?.myRecipes ?? [];

// på desktop vises preferanser alltid til venstre, så høyre side viser favoritter i stedet
// på desktop vises preferanser alltid til venstre så høyre side viser mine oppskrifter i stedet
const desktopTab: RecipeTab =
activeTab === 'preferences' ? 'favorites' : activeTab;
activeTab === 'preferences' ? 'shared' : activeTab;

const favorites = favoritesData?.myFavorites ?? [];
const drafts = draftsData?.myDrafts ?? [];
const tabRecipes =
desktopTab === 'shared'
? myRecipes
: desktopTab === 'favorites'
? favorites
: [];
: desktopTab === 'drafts'
? drafts
: favorites;

// egne oppskrifter og utkast kan slettes
const canSelect = desktopTab === 'shared' || desktopTab === 'drafts';

async function handleLogout() {
await logout();
navigate('/login');
}

// valg vises bare under mine oppskrifter
const isSelecting = selecting && desktopTab === 'shared';
const isSelecting = selecting && canSelect;

// slår valg av eller på og tømmer det som var valgt
function toggleSelecting() {
Expand Down Expand Up @@ -127,13 +131,12 @@ export default function ProfilePage() {
med deg.
</p>

{/* TODO: hent ekte tall fra backend */}
<ul className="mt-5 grid grid-cols-2 gap-x-6 gap-y-3 sm:grid-cols-4">
{/* TODO: ha en fane for delte oppskrifter hvis det blir aktuelt*/}
<ul className="mt-5 grid grid-cols-3 gap-x-6 gap-y-3">
{[
{ count: myRecipes.length, label: 'mine oppskrifter' },
{ count: drafts.length, label: 'utkast' },
{ count: favorites.length, label: 'favoritter' },
{ count: 0, label: 'delte' },
{ count: 0, label: 'delt med deg' },
].map((stat) => (
<li key={stat.label} className="flex flex-col">
<span className="font-display font-semibold text-3xl lining-nums">
Expand Down Expand Up @@ -270,7 +273,7 @@ export default function ProfilePage() {
</div>

{/* søppelbøtte som slår på valg av egne oppskrifter */}
{desktopTab === 'shared' && myRecipes.length > 0 && (
{canSelect && tabRecipes.length > 0 && (
<div className="mb-4 flex items-center justify-end gap-3">
{/* teksten og slett knappen vokser ut fra søppelbøtta */}
<AnimatePresence>
Expand All @@ -287,7 +290,7 @@ export default function ProfilePage() {
{selectedIds.length} valgt
</span>
<DeleteRecipesDialog
recipes={myRecipes.filter((recipe) =>
recipes={tabRecipes.filter((recipe) =>
selectedIds.includes(recipe.id),
)}
onDeleted={toggleSelecting}
Expand Down
11 changes: 11 additions & 0 deletions server/src/auth/session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import jwt from 'jsonwebtoken';
import type { Request, Response } from 'express';
import { GraphQLError } from 'graphql';

// navnet på cookien som holder innloggingen
const COOKIE_NAME = 'token';
Expand Down Expand Up @@ -57,3 +58,13 @@ export function getSession(req: Request): Session | null {
return null;
}
}

// stopper forspørselen hvis ingen er innloget
export function requireUser(context: Context): string {
if (!context.userId) {
throw new GraphQLError('Du må være logget inn.', {
extensions: { code: 'UNAUTHENTICATED' },
});
}
return context.userId;
}
1 change: 1 addition & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const resolvers = {
Query: { ...recipeResolvers.Query, ...userResolvers.Query },
Mutation: { ...recipeResolvers.Mutation, ...userResolvers.Mutation },
User: userResolvers.User,
Recipe: userResolvers.Recipe,
};

// for å teste med Apollo Sandbox
Expand Down
9 changes: 7 additions & 2 deletions server/src/schema/resolvers/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ export const recipeResolvers = {

return { items, totalCount };
},
recipe: async (_: unknown, args: { id: string }) => {
return prisma.recipe.findUnique({
recipe: async (_: unknown, args: { id: string }, context: Context) => {
const recipe = await prisma.recipe.findUnique({
where: { id: args.id },
include: { ingredients: true, steps: true, tags: true },
});
// utkast vises bare for den som har laget det
if (recipe?.status === 'DRAFT' && recipe.authorId !== context.userId) {
return null;
}
return recipe;
},
},
Mutation: {
Expand Down
Loading