# Hands-on: Note Taking App

## Step-by-Step Guide to Creating a Note Taking App with Vite, React, and Supabase

This guide provides a step-by-step update for enhancing the **Note-Taking App** by integrating authentication, user session management, and CRUD operations with a **Supabase database**. The updated app will feature a **Header with the app title and user email**, a **SignUp page**, and a **Dashboard** where users can **see, create, update, and delete notes**.

### Step 1: Install Node.js

Before you start, ensure you have [Node.js](https://nodejs.org/) installed. You can check your installation by running:

```bash
node -v
npm -v
```

If Node.js is not installed, download and install it from the official website.

### Step 2: Create a New Vite Project

Vite is a fast build tool for modern web applications. To create a new Vite-powered React project, run the following command at Git-bash terminal or PowerShell terminal:

```bash
npm create vite@latest react-note-taker-app --template react
```

Alternatively, if you are using Yarn or PNPM, you can use:

```bash
yarn create vite@latest react-note-taker-app --template react
```

```bash
pnpm create vite@latest react-note-taker-app --template react
```

### Step 3: Move into the Project Directory

Once the project is created, navigate into the project folder:

```bash
cd react-note-taker-app
```

### Step 4: Install Dependencies

Run the following command to install the necessary dependencies:

```bash
npm install
```

This will install all required packages specified in `package.json`.

### Step 5: Start the Development Server

Then, open Visual Studio Code IDE and open a new terminal . To start the development server and preview the application, run:

```bash
npm run dev
```

This command will provide a local development URL (e.g., `http://localhost:5173/`) where you can see your app in action.

Now, delete all unnecessary code snippet in App.jsx  to get started a new React App creation.

### Step 6: Connect to Supabase

#### a. Create a Supabase Project

1. Go to [Supabase](https://supabase.com/) and sign up.
2. Create a new project and note down the `Project URL` and `Anon Key`.

#### b. Install Supabase Client

In your project directory, install the Supabase client:

```bash
npm install @supabase/supabase-js
```

#### c. Configure Supabase

Create a <mark style="color:orange;">**`.env`**</mark> file in the root of your project and add:

```
VITE_SUPABASE_URL=your_supabase_project_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
```

Then, in `src/`<mark style="color:orange;">`supabaseClient.js`</mark>, initialize Supabase:

```jsx
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);
```

Now your project is set up with Vite, React, and Supabase!

### Step 7: Create a Database Table for NoteTaking App

Navigate to the **Supabase Dashboard** → **Table Editor** and create a new table called <mark style="color:orange;">notes</mark> with the following columns:

* `id` (UUID, Primary Key, Default: `gen_random_uuid()`)
* `title` (Text, Not Null)
* `content` (Text, Not Null)
* `date` (Timestamp, Default: `now()`)
* `iscompleted` (Boolean, Default: `false`)
* `user_id` (UUID, Foreign Key referencing auth.users, <mark style="color:red;">**Not Null**</mark>)

### Step 8: Create Authentication Components(final)

To enable user authentication, install:

```bash
npm install @supabase/auth-ui-react @supabase/auth-helpers-react
```

Then, Create `src/components/Auth.jsx` to handle **SignUp, SignIn, SignOut, Email Confirmation, and Reset Password**.

#### a. Create Auth Component for SignUp and SignIn(final)

In `src/components/Auth.jsx`, add:

{% tabs %}
{% tab title="Before" %}

```jsx
// Before
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';

export default function Auth() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session);
    });
    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
    });
  }, []);

  const signIn = async () => {
    await supabase.auth.signInWithOAuth({ provider: 'google' });
  };

  const signOut = async () => {
    await supabase.auth.signOut();
  };

  return (
    <div>
      {session ? (
        <button onClick={signOut}>Sign Out</button>
      ) : (
        <button onClick={signIn}>Sign In with Google</button>
      )}
    </div>
  );
}
```

{% endtab %}

{% tab title="After" %}

```jsx
// After
import { useState } from 'react';
import { supabase } from '../supabaseClient';
import { Button, Form, Container } from 'react-bootstrap';

const Auth = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSignUp = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) alert(error.message);
    setLoading(false);
  };

  const handleSignIn = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signInWithPassword({ email, password });
    if (error) alert(error.message);
    setLoading(false);
  };

  return (
    <Container className="mt-4">
      <h2>Sign In / Sign Up</h2>
      <Form>
        <Form.Group className="mb-3">
          <Form.Control type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required />
        </Form.Group>
        <Form.Group className="mb-3">
          <Form.Control type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required />
        </Form.Group>
        <Button variant="primary" onClick={handleSignUp} disabled={loading}>Sign Up</Button>
        <Button variant="secondary" onClick={handleSignIn} disabled={loading} className="ms-2">Sign In</Button>
      </Form>
    </Container>
  );
};

export default Auth;
```

{% endtab %}

{% tab title="final" %}

#### a. Create an Authentication Handler

Create a new file `src/auth.js` to handle user authentication:

```javascript
import { useState } from 'react';
import { supabase } from '../supabaseClient';
import { Button, Form, Container } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';

const Auth = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();

  const handleSignUp = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) alert(error.message);
    setLoading(false);
  };

  const handleSignIn = async () => {
    setLoading(true);
    const { error } = await supabase.auth.signInWithPassword({ email, password });
    if (error) alert(error.message);
    else navigate('/dashboard');
    setLoading(false);
  };

  return (
    <Container className="mt-4">
      <h2>Sign In / Sign Up</h2>
      <Form>
        <Form.Group className="mb-3">
          <Form.Control type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required />
        </Form.Group>
        <Form.Group className="mb-3">
          <Form.Control type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} required />
        </Form.Group>
        <Button variant="primary" onClick={handleSignUp} disabled={loading}>Sign Up</Button>
        <Button variant="secondary" onClick={handleSignIn} disabled={loading} className="ms-2">Sign In</Button>
      </Form>
    </Container>
  );
};

export default Auth;
```

#### b. Create Auth Component for SignUp and SignIn (optional)

In `src/auth.js`, update the code to use `auth.js` functions:

Now, the authentication logic is centralized in `auth.js`, making it reusable and maintainable. The application supports sign-up, sign-in, sign-out, password reset, and fetching the authenticated user. Let me know if you need any refinements!(optional)
{% endtab %}
{% endtabs %}

#### b. Implement Password Reset (optional)

In `src/components/ResetPassword.jsx`, add:

```jsx
import { useState } from 'react';
import { supabase } from '../supabase';

export default function ResetPassword() {
  const [email, setEmail] = useState('');

  const resetPassword = async () => {
    await supabase.auth.resetPasswordForEmail(email);
    alert('Password reset email sent!');
  };

  return (
    <div>
      <input placeholder='Email' onChange={(e) => setEmail(e.target.value)} />
      <button onClick={resetPassword}>Reset Password</button>
    </div>
  );
}
```

### Step 9: Update App Header

Modify `src/components/Header.jsx` to show the app title and logged-in user email.

```jsx
import { useEffect, useState } from 'react';
import { Navbar, Container, Button } from 'react-bootstrap';
import { supabase } from '../supabaseClient';

const Header = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      setUser(user);
    };
    fetchUser();
  }, []);

  const handleSignOut = async () => {
    await supabase.auth.signOut();
    window.location.reload();
  };

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user && (
          <span className="text-white">{user.email} <Button variant="danger" onClick={handleSignOut}>Sign Out</Button></span>
        )}
      </Container>
    </Navbar>
  );
};

export default Header;
```

### Step 10: Create the Dashboard Page

In `src/pages/Dashboard.jsx`, add:

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabaseClient';
import { Container, Button, Form } from 'react-bootstrap';
import Note from '../components/Note';

const Dashboard = () => {
  const [notes, setNotes] = useState([]);
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  useEffect(() => {
    fetchNotes();
  }, []);

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('notes').select('*');
    if (!error) setNotes(data);
  };

  const addNote = async (e) => {
    e.preventDefault();
    const { data, error } = await supabase.from('notes').insert([{ title, content, date: new Date(), iscompleted: false }]);
    if (!error) setNotes([...notes, ...data]);
    setTitle('');
    setContent('');
  };

  const updateNote = async (id, iscompleted) => {
    await supabase.from('notes').update({ iscompleted }).match({ id });
    setNotes(notes.map(note => note.id === id ? { ...note, iscompleted } : note));
  };

  const deleteNote = async (id) => {
    await supabase.from('notes').delete().match({ id });
    setNotes(notes.filter(note => note.id !== id));
  };

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <Form onSubmit={addNote}>
        <Form.Group>
          <Form.Control type="text" placeholder="Title" value={title} onChange={(e) => setTitle(e.target.value)} required />
        </Form.Group>
        <Form.Group>
          <Form.Control as="textarea" rows={3} placeholder="Content" value={content} onChange={(e) => setContent(e.target.value)} required />
        </Form.Group>
        <Button type="submit">Add Note</Button>
      </Form>
      <div className="mt-4">
        {notes.map(note => (
          <Note key={note.id} note={note} onUpdate={updateNote} onDelete={deleteNote} />
        ))}
      </div>
    </Container>
  );
};

export default Dashboard;
```

Now your NoteTaker app includes authentication, password reset, and a fully functional dashboard to create, view, update, and delete notes using Supabase!

### Step 11: Update App Header(final)

Modify `src/components/Header.jsx` to show the app title and logged-in user email, with a **Sign Out** button.

{% tabs %}
{% tab title="Before" %}

```jsx
// src/components/Header.jsx
import { useEffect, useState } from 'react';
import { Navbar, Container, Button } from 'react-bootstrap';
import { supabase } from '../supabaseClient';
import { useNavigate } from 'react-router-dom';

const Header = () => {
  const [user, setUser] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    const fetchUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      setUser(user);
    };
    fetchUser();
  }, []);

  const handleSignOut = async () => {
    await supabase.auth.signOut();
    navigate('/');
  };

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user && (
          <span className="text-white">{user.email} <Button variant="danger" onClick={handleSignOut}>Sign Out</Button></span>
        )}
      </Container>
    </Navbar>
  );
};

export default Header;
```

{% endtab %}

{% tab title="Final" %}

```jsx
// src/components/Header.jsx
import { useEffect, useState } from 'react'
import { Navbar, Container, Button } from 'react-bootstrap'
import supabase from '../supabaseClient'
import { useNavigate } from 'react-router-dom'

const Header = () => {
  const [user, setUser] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const fetchUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      setUser(user)
    }
    fetchUser()
  }, [])

  const handleSignOut = async () => {
    await supabase.auth.signOut()
    setUser(null)
    navigate('/')
  }

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user ? (
          <span className="text-white mr-2">
            {user.email}{' '}
            <Button variant="danger" onClick={handleSignOut}>
              Sign Out
            </Button>
          </span>
        ) : null}
      </Container>
    </Navbar>
  )
}

export default Header

```

{% endtab %}
{% endtabs %}

### Step 12: Create a Dashboard Component with Card Display for Notes

Modify `src/pages/Dashboard.jsx` to manage notes and display them in card format.

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabaseClient';
import { Container, Button, Card } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';

const Dashboard = () => {
  const [notes, setNotes] = useState([]);
  const navigate = useNavigate();

  useEffect(() => {
    const checkUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      if (!user) navigate('/');
    };
    checkUser();
    fetchNotes();
  }, []);

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('notes').select('*');
    if (!error) setNotes(data);
  };

  const deleteNote = async (id) => {
    await supabase.from('notes').delete().match({ id });
    setNotes(notes.filter(note => note.id !== id));
  };

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <div className="mt-4">
        {notes.map(note => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text><strong>Date:</strong> {new Date(note.date).toLocaleDateString()}</Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text><strong>Status:</strong> {note.iscompleted ? 'Completed' : 'Not Completed'}</Card.Text>
              <Button variant="danger" onClick={() => deleteNote(note.id)}><i className="bi bi-trash"></i> Delete</Button>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  );
};

export default Dashboard;
```

### Step 13: Create a Parent Component `App.jsx (final)`

Create `src/App.jsx` to integrate the **Header, Dashboard, User Authentication, and Routes**.

```jsx
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Auth from './components/Auth';
import Header from './components/Header';
import Dashboard from './pages/Dashboard';

function App() {
  return (
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Auth />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Router>
  );
}

export default App;
```

### Step 14: Create a Dashboard Component with Card Display for Notes(final)

Modify `src/pages/Dashboard.jsx` to manage notes and display them in card format.

{% tabs %}
{% tab title="Before" %}

```jsx
// src/components/Dashboard.jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabaseClient';
import { Container, Button, Card } from 'react-bootstrap';
import { useNavigate } from 'react-router-dom';

const Dashboard = () => {
  const [notes, setNotes] = useState([]);
  const navigate = useNavigate();

  useEffect(() => {
    const checkUser = async () => {
      const { data: { user } } = await supabase.auth.getUser();
      if (!user) navigate('/');
    };
    checkUser();
    fetchNotes();
  }, []);

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('notes').select('*');
    if (!error) setNotes(data);
  };

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <div className="mt-4">
        {notes.map(note => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text><strong>Date:</strong> {new Date(note.date).toLocaleDateString()}</Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text><strong>Status:</strong> {note.iscompleted ? 'Completed' : 'Not Completed'}</Card.Text>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  );
};

export default Dashboard;
```

{% endtab %}

{% tab title="After(final)" %}

````jsx
// src/components/Dashboard.jsx

import { useEffect, useState } from 'react'
import  supabase from '../supabaseClient'
import { Container, Button, Card } from 'react-bootstrap'
import { useNavigate } from 'react-router-dom'
import FormNote from '../components/FormNote'

const Dashboard = () => {
  const [notes, setNotes] = useState([])
  const [editNote, setEditNote] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const checkUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      if (!user) navigate('/')
    }
    checkUser()
    fetchNotes()
  }, [])

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('knotes').select('*')
    if (!error) setNotes(data)
  }

  const deleteNote = async (id) => {
    await supabase.from('knotes').delete().match({ id })
    setNotes(notes.filter((note) => note.id !== id))
  }

  const toggleCompletion = async (id, iscompleted) => {
    await supabase
      .from('knotes')
      .update({ iscompleted: !iscompleted })
      .match({ id })
    setNotes(
      notes.map((note) =>
        note.id === id ? { ...note, iscompleted: !iscompleted } : note,
      ),
    )
  }

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <FormNote
        refreshNotes={fetchNotes}
        existingNote={editNote}
        clearEdit={() => setEditNote(null)}
      />
      <div className="mt-4">
        {notes.map((note) => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text>
                <strong>Date:</strong>{' '}
                {new Date(note.date).toLocaleDateString()}
              </Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text>
                <strong>Status:</strong>{' '}
                {note.iscompleted ? 'Completed' : 'Not Completed'}
              </Card.Text>
              <Button
                variant="success"
                onClick={() => toggleCompletion(note.id, note.iscompleted)}>
                {note.iscompleted ? 'Mark as Incomplete' : 'Mark as Completed'}
              </Button>
              <Button
                variant="warning"
                onClick={() => setEditNote(note)}
                className="ms-2">
                Update
              </Button>
              <Button
                variant="danger"
                onClick={() => deleteNote(note.id)}
                className="ms-2">
                <i className="bi bi-trash"></i> Delete
              </Button>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  )
}

export default Dashboard

```
````

{% endtab %}
{% endtabs %}

### Step 15:  Create a Note Taking Feature(final)

In `src/components/FormNote.jsx`, add:

```jsx
// src/components/FormNote.jsx
import { useState } from 'react'
import  supabase from '../supabaseClient'
import { Form, Button } from 'react-bootstrap'

const FormNote = ({ refreshNotes }) => {
  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')

  const addNote = async (e) => {
    e.preventDefault()
    const { data, error } = await supabase
      .from('notes')
      .insert([{ title, content, date: new Date(), iscompleted: false }])
    if (!error) {
      setTitle('')
      setContent('')
      refreshNotes()
    }
  }

  return (
    <Form onSubmit={addNote} className="mb-4">
      <Form.Group className="mb-3">
        <Form.Control
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
      </Form.Group>
      <Form.Group className="mb-3">
        <Form.Control
          as="textarea"
          rows={3}
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          required
        />
      </Form.Group>
      <Button type="submit" variant="primary">
        Add Note
      </Button>
    </Form>
  )
}

export default FormNote
```

The updated app now includes:

* **A `FormNote` component to handle both note creation and updating.**
* **Dashboard functionality to create, update, and delete notes.**
* **A new Update button that allows users to edit existing notes.**

### Step 16: Run the Application

Start the development server:

```bash
npm run dev
```

#### Conclusion

The updated app now includes **a parent `App.jsx` component** that manages routing and integrates **authentication, dashboard, and header** components for a complete user experience.

### Challenge: Step A: Create a Note Taking Feature

In `src/components/FormNote.jsx`, add:

```jsx
import { useState } from 'react'
import  supabase from '../supabaseClient'
import { Form, Button } from 'react-bootstrap'

const FormNote = ({ refreshNotes }) => {
  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')

  const addNote = async (e) => {
    e.preventDefault()
    const { data, error } = await supabase
      .from('notes')
      .insert([{ title, content, date: new Date(), iscompleted: false }])
    if (!error) {
      setTitle('')
      setContent('')
      refreshNotes()
    }
  }

  return (
    <Form onSubmit={addNote} className="mb-4">
      <Form.Group className="mb-3">
        <Form.Control
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
      </Form.Group>
      <Form.Group className="mb-3">
        <Form.Control
          as="textarea"
          rows={3}
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          required
        />
      </Form.Group>
      <Button type="submit" variant="primary">
        Add Note
      </Button>
    </Form>
  )
}

export default FormNote
```

### Step B: SQL Command Operation on Supabase DB table  for Policies Setting&#x20;

```sql
// Some code
-- Enable extensions required for UUID generation
create extension if not exists "pgcrypto";

-- Create the 'notes' table
create table public.notes (
    id uuid primary key default gen_random_uuid(),
    title text not null,
    content text not null,
    date timestamp default now() not null,
    isCompleted boolean default false not null,
    user_id uuid references auth.users(id) on delete cascade not null
);

-- Enable Row Level Security (RLS) on the 'notes' table
alter table public.notes enable row level security;

-- Create policies to allow users to interact only with their own notes

-- Allow users to insert their own notes
create policy "Allow users to insert their own notes"
on public.notes
for insert
with check (auth.uid() = user_id);

-- Allow users to read only their own notes
create policy "Allow users to read their own notes"
on public.notes
for select
using (auth.uid() = user_id);

-- Allow users to update only their own notes
create policy "Allow users to update their own notes"
on public.notes
for update
using (auth.uid() = user_id);

-- Allow users to delete only their own notes
create policy "Allow users to delete their own notes"
on public.notes
for delete
using (auth.uid() = user_id);
```

### Step C: Fetch and Display Notes

In `src/components/NoteList.js`:

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';

export default function NoteList() {
  const [notes, setNotes] = useState([]);

  useEffect(() => {
    const fetchNotes = async () => {
      const { data, error } = await supabase.from('notes').select('*');
      if (!error) setNotes(data);
    };
    fetchNotes();
  }, []);

  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id}>{note.title}: {note.content}</li>
      ))}
    </ul>
  );
}
```

Now your NoteTaker app is fully functional with authentication, database integration, and note management!

### Step D: Create a Database Table in Supabase

#### a. Access Supabase Table Editor

1. Go to [Supabase](https://supabase.com/) and log in.
2. Open your project dashboard.
3. Navigate to the **Table Editor** on the left sidebar.
4. Click **New Table** .

#### b. Define Table Schema

Create a new table named `notes` with the following columns:

* **id** (UUID, Primary Key, Default: `gen_random_uuid()`, Not Null)
* **title** (Text, Not Null)
* **content** (Text, Not Null)
* **date** (Timestamp, Default: `now()`, Not Null)
* **isCompleted** (Boolean, Default: `false`, Not Null)
* **user\_id** (UUID, Foreign Key referencing `auth.users.id`, Not Null)

#### c. Save and Deploy

* Click **Save** to create the table.
* Ensure the table permissions allow `Authenticated` users to perform CRUD operations.

#### d. Configure Row-Level Security (RLS)

1. Navigate to the **Authentication** → **Policies** section.
2. Click **New Policy** and select the `notes` table.
3. Add a rule that allows users to access only their own notes:

```sql
CREATE POLICY "Enable read access for authenticated users" 
ON "public"."notes" 
FOR SELECT USING (auth.uid() = user_id);
```

4. Add policies for insert, update, and delete accordingly.

Now, your Supabase database table is set up and secured!

As an alternative better way, use the SQL command to create the `notes` table in Supabase with **Row Level Security (RLS)** enabled:

```sql
sql
-- Enable extensions required for UUID generation
create extension if not exists "pgcrypto";

-- Create the 'notes' table
create table public.notes (
    id uuid primary key default gen_random_uuid(),
    title text not null,
    content text not null,
    date timestamp default now() not null,
    isCompleted boolean default false not null,
    user_id uuid references auth.users(id) on delete cascade not null
);

-- Enable Row Level Security (RLS) on the 'notes' table
alter table public.notes enable row level security;

-- Create policies to allow users to interact only with their own notes

-- Allow users to insert their own notes
create policy "Allow users to insert their own notes"
on public.notes
for insert
with check (auth.uid() = user_id);

-- Allow users to read only their own notes
create policy "Allow users to read their own notes"
on public.notes
for select
using (auth.uid() = user_id);

-- Allow users to update only their own notes
create policy "Allow users to update their own notes"
on public.notes
for update
using (auth.uid() = user_id);

-- Allow users to delete only their own notes
create policy "Allow users to delete their own notes"
on public.notes
for delete
using (auth.uid() = user_id);
```

#### UUID Setting in RLS Policies

We have to set the column in the database table with a default of auth.uid().

* Type: uuid&#x20;
* Default Value: auth.uid()

<figure><img src="/files/EoPAOQXF1kf7jc6y7uH0" alt=""><figcaption></figcaption></figure>

#### SQL Command Explanation

* The `id` column is a UUID primary key generated using `gen_random_uuid()`.
* The `user_id` column references the `auth.users.id` field, ensuring that each note belongs to a specific authenticated user.
* **RLS is enabled** to ensure users can only access their own data.
* Four **security policies** are created to allow authenticated users to insert, select, update, and delete only their own notes.

You can execute this SQL command in the **SQL Editor** of your Supabase project to create and secure the `notes` table. 🚀

### Step E: Integrate Components into App.jsx

#### a.  See step F to create `App.jsx`

Modify `src/App.jsx` to integrate authentication and note-taking functionality:

#### b. Update `Dashboard.jsx`

Ensure `Dashboard.jsx` manages note creation, retrieval, update, and deletion:

### Final Steps

1. Run `npm run dev` to start the development server.
2. Test authentication and note management features.
3. Deploy the project using **Vercel** or **Netlify** for hosting.

Now your **Vite React Note Taker App** is fully functional with authentication and CRUD operations using Supabase! 🚀

Repeat Step 13 to accomodate sign out functionality.

### <mark style="color:red;">Note on  403 Forbidden Error</mark>

<details>

<summary>Supabase Policies</summary>

The `403 Forbidden` error typically indicates that your Supabase client does not have the necessary permissions to insert data into the `notes` table. Here are steps to troubleshoot and resolve this issue:

#### **Step 1: Check Supabase Policies**

1. Go to your **Supabase Dashboard**.
2. Navigate to **Database** > **Tables** > **notes**.
3. Click on **Row Level Security (RLS)**.
4. If RLS is **enabled**, ensure you have a policy that allows authenticated users to insert data. If there's no policy, add the following policy:

   **Create a policy for INSERT**

   * **Name**: Allow Insert for Authenticated Users
   * **Target**: INSERT
   * **Expression**: `auth.uid() IS NOT NULL <--- default = auth.uid()`

   This policy ensures only signed-in users can insert new notes.

#### **Step 2: Verify Supabase API Key Usage**

1. Open your `.env` file and confirm that you are using the correct **Anon Key**:

   ```jsx
   /.env file
   VITE_SUPABASE_URL=your_supabase_url
   VITE_SUPABASE_ANON_KEY=your_supabase_anon_key
   ```
2. If your Supabase project requires **Service Role Key** for inserting data, switch to using the **Service Role Key** in a backend function instead of using it directly in frontend code (which is unsafe).

</details>

### Step F: Integrate Components into App.jsx

#### a. Create `App.jsx`

Modify `src/App.jsx` to integrate authentication and note-taking functionality:

{% tabs %}
{% tab title="Before" %}

```jsx
//src/App.jsx
import { useState, useEffect } from 'react';
import { supabase } from './supabase';
import Auth from './components/Auth';
import Dashboard from './pages/Dashboard';

export default function App() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session);
    });
    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
    });
  }, []);

  return (
    <div>
      {session ? <Dashboard session={session} /> : <Auth />}
    </div>
  );
}
```

{% endtab %}

{% tab title="Final" %}

````jsx
// src/App.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Auth from './components/Auth';
import Header from './components/Header';
import Dashboard from './pages/Dashboard';

function App() {
  return (
    <Router>
      <Header />
      <Routes>
        <Route path="/" element={<Auth />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Router>
  );
}

export default App;

```
````

{% endtab %}
{% endtabs %}

#### b. Update `Dashboard.jsx`

Ensure `Dashboard.jsx` includes a sign-out button and correctly saves notes:

{% tabs %}
{% tab title="Before" %}

```jsx
// src/pages/Dashboard.jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';

export default function Dashboard({ session }) {
  const [notes, setNotes] = useState([]);
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  useEffect(() => {
    const fetchNotes = async () => {
      const { data, error } = await supabase.from('notes').select('*').eq('user_id', session.user.id);
      if (!error) setNotes(data);
    };
    fetchNotes();
  }, [session]);

  const createNote = async () => {
    const { data, error } = await supabase.from('notes').insert([
      { title, content, user_id: session.user.id }
    ]);
    if (!error) setNotes([...notes, data[0]]);
  };

  const signOut = async () => {
    await supabase.auth.signOut();
  };

  return (
    <div>
      <h1>Dashboard</h1>
      <button onClick={signOut}>Sign Out</button>
      <input placeholder='Title' value={title} onChange={(e) => setTitle(e.target.value)} />
      <textarea placeholder='Content' value={content} onChange={(e) => setContent(e.target.value)} />
      <button onClick={createNote}>Create Note</button>
      <ul>
        {notes.map((note) => (
          <li key={note.id}>{note.title}: {note.content}</li>
        ))}
      </ul>
    </div>
  );
}
```

{% endtab %}

{% tab title="After" %}

````jsx
// src/pages/Dashboard.jsx
import { useEffect, useState } from 'react'
import  supabase from '../supabaseClient'
import { Container, Button, Card } from 'react-bootstrap'
import { useNavigate } from 'react-router-dom'
import FormNote from '../components/FormNote'

const Dashboard = () => {
  const [notes, setNotes] = useState([])
  const [editNote, setEditNote] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const checkUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      if (!user) navigate('/')
    }
    checkUser()
    fetchNotes()
  }, [])

  const fetchNotes = async () => {
    const { data, error } = await supabase.from('knotes').select('*')
    if (!error) setNotes(data)
  }

  const deleteNote = async (id) => {
    await supabase.from('knotes').delete().match({ id })
    setNotes(notes.filter((note) => note.id !== id))
  }

  const toggleCompletion = async (id, iscompleted) => {
    await supabase
      .from('knotes')
      .update({ iscompleted: !iscompleted })
      .match({ id })
    setNotes(
      notes.map((note) =>
        note.id === id ? { ...note, iscompleted: !iscompleted } : note,
      ),
    )
  }

  return (
    <Container className="mt-4">
      <h2>Manage Your Notes</h2>
      <FormNote
        refreshNotes={fetchNotes}
        existingNote={editNote}
        clearEdit={() => setEditNote(null)}
      />
      <div className="mt-4">
        {notes.map((note) => (
          <Card key={note.id} className="mb-3">
            <Card.Body>
              <Card.Title>{note.title}</Card.Title>
              <Card.Text>
                <strong>Date:</strong>{' '}
                {new Date(note.date).toLocaleDateString()}
              </Card.Text>
              <Card.Text>{note.content}</Card.Text>
              <Card.Text>
                <strong>Status:</strong>{' '}
                {note.iscompleted ? 'Completed' : 'Not Completed'}
              </Card.Text>
              <Button
                variant="success"
                onClick={() => toggleCompletion(note.id, note.iscompleted)}>
                {note.iscompleted ? 'Mark as Incomplete' : 'Mark as Completed'}
              </Button>
              <Button
                variant="warning"
                onClick={() => setEditNote(note)}
                className="ms-2">
                Update
              </Button>
              <Button
                variant="danger"
                onClick={() => deleteNote(note.id)}
                className="ms-2">
                <i className="bi bi-trash"></i> Delete
              </Button>
            </Card.Body>
          </Card>
        ))}
      </div>
    </Container>
  )
}

export default Dashboard

```
````

{% endtab %}
{% endtabs %}

c.  Update `Header.jsx`

```jsx
// src/components/Header.jsx
import { useEffect, useState } from 'react'
import { Navbar, Container, Button } from 'react-bootstrap'
import supabase from '../supabaseClient'
import { useNavigate } from 'react-router-dom'

const Header = () => {
  const [user, setUser] = useState(null)
  const navigate = useNavigate()

  useEffect(() => {
    const fetchUser = async () => {
      const {
        data: { user },
      } = await supabase.auth.getUser()
      setUser(user)
    }
    fetchUser()
  }, [])

  const handleSignOut = async () => {
    await supabase.auth.signOut()
    setUser(null)
    navigate('/')
  }

  return (
    <Navbar bg="dark" variant="dark">
      <Container>
        <Navbar.Brand>Note Taking App</Navbar.Brand>
        {user ? (
          <span className="text-white mr-2">
            {user.email}{' '}
            <Button variant="danger" onClick={handleSignOut}>
              Sign Out
            </Button>
          </span>
        ) : null}
      </Container>
    </Navbar>
  )
}

export default Header

```

### Step G: Modify `FormNote.jsx` to Include Date Input and Completion Checkbox

In `src/components/FormNote.jsx`, update the form to include an input for the creation date and a checkbox for task completion:

{% tabs %}
{% tab title="Before" %}

```jsx
// src/components/FormNote.jsx
import { useState } from 'react';
import { supabase } from '../supabase';

export default function NoteForm({ onNoteAdded }) {
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
  const [date, setDate] = useState(new Date().toISOString().split('T')[0]);
  const [isCompleted, setIsCompleted] = useState(false);

  const createNote = async () => {
    const { data, error } = await supabase.from('notes').insert([
      { title, content, date, isCompleted }
    ]);
    if (!error) {
      onNoteAdded(data[0]);
      setTitle('');
      setContent('');
      setDate(new Date().toISOString().split('T')[0]);
      setIsCompleted(false);
    }
  };

  return (
    <div>
      <input placeholder='Title' value={title} onChange={(e) => setTitle(e.target.value)} />
      <textarea placeholder='Content' value={content} onChange={(e) => setContent(e.target.value)} />
      <input type='date' value={date} onChange={(e) => setDate(e.target.value)} />
      <label>
        <input type='checkbox' checked={isCompleted} onChange={(e) => setIsCompleted(e.target.checked)} />
        Completed
      </label>
      <button onClick={createNote}>Create Note</button>
    </div>
  );
}
```

{% endtab %}

{% tab title="After" %}

````jsx
// src/components/FormNote.jsx 
import { useState } from 'react'
import  supabase from '../supabaseClient'
import { Form, Button } from 'react-bootstrap'

const FormNote = ({ refreshNotes, existingNote, clearEdit }) => {
  const [title, setTitle] = useState(existingNote ? existingNote.title : '')
  const [content, setContent] = useState(
    existingNote ? existingNote.content : '',
  )

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (existingNote) {
      await supabase
        .from('knotes')
        .update({ title, content })
        .match({ id: existingNote.id })
    } else {
      await supabase
        .from('knotes')
        .insert([{ title, content, date: new Date(), iscompleted: false }])
    }
    setTitle('')
    setContent('')
    refreshNotes()
    if (existingNote) clearEdit()
  }

  return (
    <Form onSubmit={handleSubmit} className="mb-4">
      <Form.Group className="mb-3">
        <Form.Control
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          required
        />
      </Form.Group>
      <Form.Group className="mb-3">
        <Form.Control
          as="textarea"
          rows={3}
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
          required
        />
      </Form.Group>
      <Button type="submit" variant="primary">
        {existingNote ? 'Update Note' : 'Add Note'}
      </Button>
      {existingNote && (
        <Button variant="secondary" onClick={clearEdit} className="ms-2">
          Cancel
        </Button>
      )}
    </Form>
  )
}

export default FormNote

```
````

{% endtab %}
{% endtabs %}

### Step H: Modify `NoteList.jsx` to Display Date, Completion Checkbox, and Delete Button

In `src/components/NoteList.jsx`, update the list to show the note creation date, allow marking completion, and add a delete button:

```jsx
import { useEffect, useState } from 'react';
import { supabase } from '../supabase';
import 'bootstrap-icons/font/bootstrap-icons.css';

export default function NoteList() {
  const [notes, setNotes] = useState([]);

  useEffect(() => {
    const fetchNotes = async () => {
      const { data, error } = await supabase.from('notes').select('*');
      if (!error) setNotes(data);
    };
    fetchNotes();
  }, []);

  const deleteNote = async (id) => {
    await supabase.from('notes').delete().match({ id });
    setNotes(notes.filter(note => note.id !== id));
  };

  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id}>
          <h3>{note.title}</h3>
          <p>{note.content}</p>
          <p><strong>Date Created:</strong> {note.date}</p>
          <label>
            <input type='checkbox' checked={note.isCompleted} readOnly />
            Completed
          </label>
          <button onClick={() => deleteNote(note.id)}>
            <i className='bi bi-trash'></i>
          </button>
        </li>
      ))}
    </ul>
  );
}
```

### Step I ::

### Step J: Run and Test Your Application

1. Start your development server with `npm run dev`.
2. Add new notes using the form, ensuring they store `date` and `isCompleted`.
3. Ensure the delete button removes notes successfully.
4. Verify that notes are stored and retrieved correctly from Supabase.

Now your **Vite React Note Taker App** includes a date input for note creation, a checkbox for marking completion, and a delete button using Bootstrap icons! 🚀


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reactjs.koida.tech/database-in-application/hands-on-note-taking-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
