Skip to content

Commit

Permalink
[lists] add favorite change logic
Browse files Browse the repository at this point in the history
  • Loading branch information
sabovyan committed Dec 7, 2023
1 parent 50ca053 commit baf718e
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 25 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
".next/**/*"
],
"rules": {
"no-unused-vars": "error",
"import/order": [
"error",
{
Expand Down
43 changes: 25 additions & 18 deletions app/actions/list.action.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
'use server';

import { revalidateTag } from 'next/cache';
// import { revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';

import { auth } from '@/lib/auth';
import { prisma } from '@/lib/prisma';

const schema = z.object({
const addListSchema = z.object({
name: z.string(),
spaceId: z.string()
});

export const addList = async (formData: FormData) => {
const { name, spaceId } = schema.parse({
const { name, spaceId } = addListSchema.parse({
name: formData.get('name'),
spaceId: formData.get('spaceId')
});
Expand All @@ -23,23 +23,30 @@ export const addList = async (formData: FormData) => {
const newList = await prisma.list.create({
data: {
name,
Space: {
connect: {
id: spaceId
}
},
creator: {
connect: {
id: session?.user.id
}
}
Space: { connect: { id: spaceId } },
creator: { connect: { id: session?.user.id } }
},
select: {
id: true
}
select: { id: true }
});

revalidateTag(`/spaces/${spaceId}`);

// revalidateTag(`/spaces/${spaceId}`);
redirect(`/spaces/${spaceId}/lists/${newList.id}`);
};

const changeFaveSchema = z.object({
listId: z.string(),
favorite: z.boolean()
});

export const changeFaveStatus = async (formData: FormData) => {
const { listId: id, favorite } = changeFaveSchema.parse({
listId: formData.get('listId'),
spaceId: formData.get('spaceId'),
favorite: formData.get('favorite') === 'on'
});

await prisma.list.update({
where: { id },
data: { favorite: favorite }
});
};
14 changes: 11 additions & 3 deletions app/spaces/[spaceId]/lists/[listId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { notFound } from 'next/navigation';

import { FavoriteListButton } from '@/components/FavoriteListButton';
import { prisma } from '@/lib/prisma';

import { SpaceItemParams } from '../../page';
Expand All @@ -17,8 +18,15 @@ export default async function List({ params }: { params: ListItemParams }) {
}

return (
<div>
<h1>{params.spaceId}</h1>
</div>
<main>
<header className="flex gap-4">
<h2 className="text-2xl">{list.name}</h2>
<FavoriteListButton
spaceId={params.spaceId}
listId={list.id}
isFave={list.favorite}
/>
</header>
</main>
);
}
10 changes: 8 additions & 2 deletions app/spaces/[spaceId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Link from 'next/link';
import { notFound } from 'next/navigation';

import { FavoriteListButton } from '@/components/FavoriteListButton';
import { prisma } from '@/lib/prisma';

export type SpaceItemParams = { spaceId: string };
Expand Down Expand Up @@ -41,14 +42,19 @@ export default async function SingleSpace({
{space.Lists.map((list) => (
<li
key={list.id}
className="text-xl text-center min-w-[200px] p-2 rounded-lg border"
className="text-xl text-center min-w-[200px] p-2 rounded-lg border flex justify-between"
>
<Link
href={`/spaces/${space.id}/lists/${list.id}`}
className="block"
className="block flex-grow"
>
{list.name}
</Link>
<FavoriteListButton
spaceId={space.id}
listId={list.id}
isFave={list.favorite}
/>
</li>
))}
</ul>
Expand Down
59 changes: 59 additions & 0 deletions components/FavoriteListButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client';

import { useOptimistic, useState } from 'react';

import { changeFaveStatus } from '@/app/actions/list.action';

import { Button } from './ui/button';

export function FavoriteListButton({
isFave = false,
spaceId,
listId
}: {
isFave?: boolean | null;
listId: string;
spaceId: string;
}) {
const [favoriteStatus, setfavoriteStatus] = useState(isFave);

const [favoriteOptimisticStatus, updateOptimisticStatus] = useOptimistic<
typeof favoriteStatus,
typeof favoriteStatus
>(favoriteStatus, (_, newState) => {
return newState;
});

return (
<form
action={async (formData: FormData) => {
const faveStatus = !!formData.get('favorite');

updateOptimisticStatus(faveStatus);

await changeFaveStatus(formData);
setfavoriteStatus(faveStatus);
}}
>
<input type="text" name="listId" defaultValue={listId} hidden readOnly />
<input
type="text"
name="spaceId"
defaultValue={spaceId}
hidden
readOnly
/>
<input
type="checkbox"
name="favorite"
checked={!favoriteOptimisticStatus}
hidden
readOnly
/>

<Button variant="secondary" size="icon">
{favoriteOptimisticStatus ? '★' : '☆'}
</Button>
</form>
);
}
1 change: 1 addition & 0 deletions components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint no-unused-vars: 0 */
import { headers } from 'next/headers';
import { userAgent } from 'next/server';

Expand Down
3 changes: 2 additions & 1 deletion lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import NextAuth, { DefaultSession } from 'next-auth';
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';

import { prisma } from '@/lib/prisma';

declare module '../node_modules/.pnpm/@[email protected]/node_modules/@auth/core/types.d.ts' {
// eslint-disable-next-line
interface Session {
user: {
id: string;
Expand Down
1 change: 0 additions & 1 deletion tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Config } from 'tailwindcss';

import { fontFamily } from 'tailwindcss/defaultTheme';

const config: Config = {
Expand Down

0 comments on commit baf718e

Please sign in to comment.