Skip to content
13 changes: 7 additions & 6 deletions src/routes/docs/tutorials/nextjs/step-4/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ Create a new file `hooks/useAuth.ts` and add the following code.
// hooks/useAuth.ts

import { useState, useEffect } from 'react';
import { account } from '../lib/appwrite';
import { account } from '@/lib/appwrite';
import { ID } from 'appwrite';
import type { Models } from 'appwrite';
import { useRouter } from 'next/navigation';

export function useAuth() {
const [current, setCurrent] = useState<Models.Session | null>(null);
const [current, setCurrent] = useState<Models.User<Models.Preferences> | null>(null);
const [loading, setLoading] = useState(true);
const router = useRouter();

Expand All @@ -46,16 +46,17 @@ export function useAuth() {
};

const login = async (email: string, password: string): Promise<void> => {
const session = await account.createEmailPasswordSession({
await account.createEmailPasswordSession({
email,
password
});
setCurrent(session);
const user = await account.get();
setCurrent(user);
router.push('/');
};

const logout = async (): Promise<void> => {
await account.deleteSession('current');
await account.deleteSession({ sessionId: 'current' });
setCurrent(null);
router.push('/');
};
Expand Down Expand Up @@ -97,7 +98,7 @@ We will define functions to handle form submissions and show either a signup or
'use client';

import { useState } from 'react';
import { useAuth } from '../../hooks/useAuth';
import { useAuth } from '@/hooks/useAuth';
import AuthForm from '../../components/AuthForm';

export default function LoginPage() {
Expand Down
4 changes: 2 additions & 2 deletions src/routes/docs/tutorials/nextjs/step-5/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Create a new file `src/components/Navbar.tsx` and add the code below.
'use client';

import Link from 'next/link';
import { useAuth } from '../hooks/useAuth';
import { useAuth } from '@/hooks/useAuth';

export default function Navbar() {
const { current, logout } = useAuth();
Expand All @@ -28,7 +28,7 @@ export default function Navbar() {
<h3 className="u-stretch eyebrow-heading-1">Idea Tracker</h3>
{current ? (
<div className="main-header-end u-margin-inline-end-16">
<p>{current.providerUid}</p>
<p>{current.name || current.email}</p>
<button
className="button"
type="button"
Expand Down
49 changes: 28 additions & 21 deletions src/routes/docs/tutorials/nextjs/step-6/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ We will add a new hook, `useIdeas`, to handle this functionality.
Create a new file `hooks/useIdeas.ts` and add the following code.

```ts
"use client";
// hooks/useIdeas.ts

import { useState, useEffect } from 'react';
import { ID, Query, Permission, type Models } from 'appwrite';
import { tablesDB } from '../lib/appwrite';
import {useState, useEffect} from 'react';
import {ID, Query, Permission, type Models} from 'appwrite';
import {tablesDB} from '@/lib/appwrite';

const databaseId = process.env.NEXT_PUBLIC_DATABASE_ID!;
const tableId = process.env.NEXT_PUBLIC_TABLE_ID!;
Expand All @@ -78,12 +79,15 @@ export function useIdeas() {
// Fetch the 10 most recent ideas from the database
const fetch = async (): Promise<void> => {
try {
const response = await tablesDB.listRows(
databaseId,
tableId,
[Query.orderDesc('$createdAt'), Query.limit(queryLimit)]
);
setCurrent(response.rows as Idea[]);
const response = await tablesDB.listRows<Idea>({
databaseId: databaseId,
tableId: tableId,
queries: [
Query.orderDesc('$createdAt'),
Query.limit(queryLimit)
],
});
setCurrent(response.rows);
} catch (error) {
console.error('Error fetching ideas:', error);
} finally {
Expand All @@ -92,28 +96,31 @@ export function useIdeas() {
};

// Add new idea to the database
const add = async (idea: Omit<Idea, '$id' | '$createdAt' | '$updatedAt' | '$permissions'>): Promise<void> => {
const add = async (ideaData: Omit<Idea, keyof Models.Row>): Promise<void> => {
try {
const response = await tablesDB.createRow(
databaseId,
tableId,
ID.unique(),
idea,
[
const response = await tablesDB.createRow<Idea>({
databaseId: databaseId,
tableId: tableId,
rowId: ID.unique(),
data: ideaData,
permissions: [
Permission.read('any'),
Permission.update(`user:${idea.userId}`),
Permission.delete(`user:${idea.userId}`)
Permission.update('user:' + ideaData.userId),
Permission.delete('user:' + ideaData.userId)
]
);
setCurrent(prev => [response as Idea, ...prev].slice(0, queryLimit));
});
setCurrent((prev) => {
const newList = [response, ...prev];
return newList.slice(0, queryLimit);
});
} catch (error) {
console.error('Error adding idea:', error);
}
};

const remove = async (id: string): Promise<void> => {
try {
await tablesDB.deleteRow(databaseId, tableId, id);
await tablesDB.deleteRow({databaseId: databaseId, tableId: tableId, rowId: id});
await fetch(); // Refetch ideas to ensure we have 10 items
} catch (error) {
console.error('Error removing idea:', error);
Expand Down
62 changes: 46 additions & 16 deletions src/routes/docs/tutorials/nextjs/step-7/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,44 @@ Create a new file `src/components/IdeasForm.tsx` and add the following code.
// src/components/IdeasForm.tsx
'use client';

import { useIdeas } from '../hooks/useIdeas';
import { useAuth } from '../hooks/useAuth';
import { useAuth } from '@/hooks/useAuth';
import type {Models} from "appwrite";

export default function IdeasForm() {
const { add } = useIdeas();
interface Idea extends Models.Row {
title: string;
description: string;
userId: string;
}

interface IdeasFormProps {
onAdd: (idea: Omit<Idea, keyof Models.Row>) => Promise<void>;
}

export default function IdeasForm({ onAdd }: IdeasFormProps) {
const { current: user } = useAuth();

const handleAddIdea = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

if (!user) return;

const form = event.target as HTMLFormElement;
const formData = new FormData(form);

if (!user) return;


const postIdeaData = {
userId: user.userId,
userId: user.$id,
title: formData.get('title') as string,
description: formData.get('description') as string,
};

await add(postIdeaData);
form.reset();
try {
await onAdd(postIdeaData);
form.reset();
} catch (error) {
console.error("Failed to add idea:", error);
}
};

if (!user) {
Expand Down Expand Up @@ -98,11 +114,22 @@ Now we need to display the ideas from the database. Create a new file `src/compo
// src/components/IdeasList.tsx
'use client';

import { useIdeas } from '../hooks/useIdeas';
import { useAuth } from '../hooks/useAuth';
import {useAuth} from '@/hooks/useAuth';
import {Models} from "appwrite";

interface Idea extends Models.Row {
title: string;
description: string;
userId: string;
}

export default function IdeasList() {
const { current: ideas, loading, remove } = useIdeas();
interface IdeasListProps {
ideas: Idea[];
loading: boolean;
onRemove: (id: string) => Promise<void>;
}

export default function IdeasList({ ideas, loading, onRemove }: IdeasListProps) {
const { current: user } = useAuth();

if (loading) {
Expand Down Expand Up @@ -132,9 +159,9 @@ export default function IdeasList() {
</p>
)}
</div>
{user && user.userId === idea.userId && (
{user && user.$id === idea.userId && (
<button
onClick={() => remove(idea.$id)}
onClick={() => onRemove(idea.$id)}
className="button is-text u-padding-inline-8"
aria-label="Delete idea"
>
Expand All @@ -155,11 +182,14 @@ export default function IdeasList() {
Now let's update the home page to use our new components. Update `src/app/page.tsx`:

```tsx
'use client';
// src/app/page.tsx
import IdeasForm from '../components/IdeasForm';
import IdeasList from '../components/IdeasList';
import {useIdeas} from "@/hooks/useIdeas";

export default function Home() {
const { current: ideas, loading, add, remove } = useIdeas();
return (
<main className="u-padding-16">
<div className="container">
Expand All @@ -168,8 +198,8 @@ export default function Home() {
Track all your side project ideas in one place.
</p>
</div>
<IdeasForm />
<IdeasList />
<IdeasForm onAdd={add} />
<IdeasList ideas={ideas} loading={loading} onRemove={remove} />
</main>
);
}
Expand Down